1 /** 2 3 Set implemented as hash table 4 5 Inherits @nogc and @safe properties from key properties. 6 7 Implements next set ops 8 $(UL create - fill set from range) 9 $(UL add - add item to set; O(1)) 10 $(UL remove - remove item from set if present; O(1)) 11 $(UL length - number of items in set; O(1)) 12 $(UL join - join sets; O(N)) 13 $(UL intersection - create intersection of two sets; O(N)) 14 $(UL difference - create difference of two sets; O(N)) 15 $(UL iterate - create iterator over set items;) 16 $(UL in - if element presented in set; O(1)) 17 */ 18 module cachetools.containers.set; 19 20 import std.algorithm; 21 import std.range; 22 23 import cachetools.containers.hashmap; 24 25 /// 26 /// create set from input range 27 /// 28 auto set(R)(R r) if (isInputRange!R) { 29 alias K = ElementType!R; 30 Set!K result; 31 r.each!(k => result.add(k)); 32 return result; 33 } 34 35 /// Set structure 36 struct Set(K) { 37 private: 38 HashMap!(K, bool) _map; 39 40 public: 41 42 /// Fill set from range 43 void create(R)(R range) { 44 _map.clear; 45 range.each!(k => _map.put(k, true)); 46 } 47 /// add element to set 48 void add(K)(K k) { 49 _map.put(k, true); 50 } 51 /// remove element from set 52 void remove(K)(K k) { 53 _map.remove(k); 54 } 55 /// number of items in set 56 auto length() const { 57 return _map.length; 58 } 59 /// join other set to this set 60 void join(K)(Set!K other) { 61 if ( other.length == 0 ) return; 62 63 foreach(ref b; other._map._buckets.bs) { 64 if ( b.hash >= ALLOCATED_HASH ) 65 _map.put(b.key, true); 66 } 67 } 68 /// create intersection of two sets 69 auto intersection(K)(Set!K other) { 70 Set!K result; 71 72 if (other.length == 0 || this.length == 0 ) return result; 73 74 if ( other.length < _map.length ) { 75 foreach (ref bucket; other._map._buckets.bs) { 76 if ( bucket.hash >= ALLOCATED_HASH && bucket.key in _map ) 77 result.add(bucket.key); 78 } 79 } else { 80 foreach (ref bucket; _map._buckets.bs) { 81 if (bucket.hash >= ALLOCATED_HASH && bucket.key in other._map) 82 result.add(bucket.key); 83 } 84 } 85 return result; 86 } 87 /// create difference of two sets 88 auto difference(K)(Set!K other) { 89 Set!K result; 90 if ( other.length == 0 ) return this; 91 foreach (ref bucket; _map._buckets.bs) { 92 if (bucket.hash >= ALLOCATED_HASH && bucket.key !in other._map) 93 result.add(bucket.key); 94 } 95 return result; 96 } 97 /// iterate over items 98 auto iterate() { 99 return _map.byKey; 100 } 101 /// if element present in set 102 bool opBinaryRight(string op)(K k) inout if (op=="in") { 103 return k in _map?true:false; 104 } 105 } 106 107 /// 108 @safe @nogc unittest { 109 import std.stdio; 110 111 Set!string s; 112 s.add("hello"); 113 assert(s.length == 1); 114 assert(equal(s.iterate, only("hello"))); 115 s.remove("hello"); 116 assert(s.length == 0); 117 s.remove("hello"); 118 assert(s.length == 0); 119 120 s.create(only("hello", "hello", "world")); 121 assert(s.length == 2); 122 123 s.join(set(only("and", "bye"))); 124 assert(s.length == 4); 125 126 auto other = set(only("and", "bye", "!")); 127 auto cross0 = s.intersection(other); 128 assert("bye" in cross0); 129 assert("!" !in cross0); 130 auto cross1 = other.intersection(s); 131 assert(cross0.length == cross1.length); 132 assert("and" in cross0 && "and" in cross1); 133 assert("bye" in cross0 && "bye" in cross1); 134 135 auto nums = set(iota(10)); 136 auto someNums = nums.difference(set(only(1,2,3))); 137 assert(0 in someNums); 138 assert(1 !in someNums); 139 140 bool f(const Set!string s) { 141 return "yes" in s; 142 } 143 144 Set!string ss; 145 ss.add("yes"); 146 f(ss); 147 assert("yes" in ss); 148 }