1 /*
2  * Copyright (c) 2012-2018 The ANTLR Project. All rights reserved.
3  * Use of this file is governed by the BSD 3-clause license that
4  * can be found in the LICENSE.txt file in the project root.
5  */
6 
7 module antlr.v4.runtime.misc.Array2DHashSet;
8 
9 import std.array;
10 import std.variant;
11 import std.stdio;
12 import std.conv;
13 import std.math : floor;
14 import std.container.array;
15 import std.algorithm.mutation : remove;
16 import std.algorithm.searching : canFind;
17 import antlr.v4.runtime.misc;
18 
19 // Class Template Array2DHashSet
20 /**
21  * Set implementation with closed hashing (open addressing).
22  */
23 class Array2DHashSet(T)
24 {
25 
26     public static immutable int INITAL_CAPACITY = 16;
27 
28     public static immutable int INITAL_BUCKET_CAPACITY = 8;
29 
30     public static immutable double LOAD_FACTOR = 0.75;
31 
32     protected size_t function(Object o) @trusted nothrow hashOfFp;
33 
34     protected bool function(Object a, Object b) opEqualsFp;
35 
36     protected T[][] buckets;
37 
38     /**
39      * @uml
40      * How many elements in set
41      */
42     protected int n = 0;
43 
44     /**
45      * @uml
46      * when to expand
47      */
48     protected int threshold = cast(int)(INITAL_CAPACITY * LOAD_FACTOR);
49 
50     /**
51      * @uml
52      * jump by 4 primes each expand or whatever
53      */
54     protected int currentPrime = 1;
55 
56     protected int initialBucketCapacity = INITAL_BUCKET_CAPACITY;
57 
58     public this()
59     {
60         this(null, null, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
61     }
62 
63     public this(size_t function(Object o) @trusted nothrow hashOfFp, bool function(Object a, Object b) opEqualsFp)
64     {
65         this(hashOfFp, opEqualsFp, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
66     }
67 
68     public this(size_t function(Object o) @trusted nothrow hashOfFp, bool function(Object a, Object b) opEqualsFp,
69                 int initialCapacity, int initialBucketCapacity)
70     {
71 	if (hashOfFp is null || opEqualsFp is null) {
72             this.hashOfFp = &ObjectEqualityComparator.hashOf;
73             this.opEqualsFp = &ObjectEqualityComparator.opEquals;
74         }
75         else {
76             this.hashOfFp = hashOfFp;
77             this.opEqualsFp = opEqualsFp;
78         }
79         this.buckets = createBuckets(initialCapacity);
80         this.initialBucketCapacity = initialBucketCapacity;
81     }
82 
83     /**
84      * @uml
85      * Add {@code o} to set if not there; return existing value if already
86      * there. This method performs the same operation as {@link #add} aside from
87      * the return value.
88      * @final
89      */
90     public final T getOrAdd(T o)
91     {
92         if (n > threshold) {
93             expand();
94         }
95         return getOrAddImpl(o);
96     }
97 
98     protected T getOrAddImpl(T o)
99     {
100         auto b = getBucket(o);
101         T[] bucket = buckets[b];
102         // NEW BUCKET
103         if (bucket is null) {
104             bucket = createBucket(initialBucketCapacity);
105             bucket[0] = o;
106             buckets[b] = bucket;
107             n++;
108             return o;
109         }
110 
111         // LOOK FOR IT IN BUCKET
112         for (int i=0; i < bucket.length; i++) {
113             auto existing = bucket[i];
114             if (!existing) { // empty slot; not there, add.
115                 bucket[i] = o;
116                 n++;
117                 return o;
118             }
119             if (opEqualsFp(existing, o)) {
120                 return existing; // found existing, quit
121             }
122         }
123 
124         // FULL BUCKET, expand and add to end
125         auto oldLength = bucket.length;
126         bucket.length = bucket.length * 2;
127         buckets[b] = bucket;
128         bucket[oldLength] = o; // add to end
129         n++;
130         return o;
131     }
132 
133     public T get(T o)
134     {
135 	if (o is null)
136             return o;
137         T nullElement;
138         auto b = getBucket(o);
139         T[] bucket = buckets[b];
140         if (bucket is null)
141             return nullElement; // no bucket
142         foreach (e; bucket) {
143             if (e is null)
144                 return nullElement; // empty slot; not there
145             if (opEqualsFp(e, o))
146                 return e;
147         }
148         return nullElement;
149     }
150 
151     /**
152      * @uml
153      * @final
154      */
155     protected final size_t getBucket(T o)
156     {
157         return hashOfFp(o) & (buckets.length - 1); // assumes length is power of 2
158     }
159 
160     /**
161      * @uml
162      * @override
163      * @safe
164      * @nothrow
165      */
166     public override size_t toHash() @safe nothrow
167     {
168 	size_t hash = MurmurHash.initialize();
169         foreach (bucket; buckets) {
170             if (bucket is null) continue;
171             foreach (o; bucket) {
172                 if (o is null) break;
173                 hash = MurmurHash.update(hash, hashOfFp(o));
174             }
175         }
176         hash = MurmurHash.finish(hash, size());
177         return hash;
178     }
179 
180     /**
181      * @uml
182      * @override
183      */
184     public override bool opEquals(Object o)
185     {
186 	if (o is this) return true;
187         if (o.classinfo != Array2DHashSet.classinfo) return false;
188         Array2DHashSet!T other = cast(Array2DHashSet!T)o;
189         if (other.size() != size()) return false;
190         bool same = this.containsAll(other);
191         return same;
192     }
193 
194     protected void expand()
195     {
196 	auto old = buckets;
197         currentPrime += 4;
198         int newCapacity = to!int(buckets.length) * 2;
199         auto newTable = createBuckets(newCapacity);
200         int[] newBucketLengths = new int[newTable.length];
201         buckets = newTable;
202         threshold = cast(int)(newCapacity * LOAD_FACTOR);
203         //		System.out.println("new size="+newCapacity+", thres="+threshold);
204         // rehash all existing entries
205         int oldSize = size();
206         foreach (bucket; old) {
207             if (bucket is null) {
208                 continue;
209             }
210             foreach (o; bucket) {
211                 if (o is null) {
212                     break;
213                 }
214                 auto b = getBucket(o);
215                 int bucketLength = newBucketLengths[b];
216                 T[] newBucket;
217                 if (bucketLength == 0) {
218                     // new bucket
219                     newBucket = createBucket(initialBucketCapacity);
220                     newTable[b] = newBucket;
221                 }
222                 else {
223                     newBucket = newTable[b];
224                     if (bucketLength == newBucket.length) {
225                         // expand
226                         newBucket.length = newBucket.length * 2;
227                         newTable[b] = newBucket;
228                     }
229                 }
230 
231                 newBucket[bucketLength] = o;
232                 newBucketLengths[b]++;
233             }
234         }
235         assert(n == oldSize);
236     }
237 
238     /**
239      * @uml
240      * @final
241      */
242     public final bool add(T t)
243     {
244 	T existing = getOrAdd(t);
245         return existing == t;
246     }
247 
248     /**
249      * @uml
250      * @final
251      */
252     public final int size()
253     {
254 	return n;
255     }
256 
257     /**
258      * @uml
259      * @final
260      */
261     public final bool isEmpty()
262     {
263         return n==0;
264     }
265 
266     /**
267      * @uml
268      * @final
269      */
270     public final bool contains(T o)
271     {
272         return containsFast(o);
273     }
274 
275     public bool containsFast(T obj)
276     {
277 	if (obj is null) {
278             return false;
279         }
280         return get(obj) !is null;
281     }
282 
283     public T[] toArray()
284     {
285 	T[] a;
286         foreach (bucket; buckets) {
287             if (bucket is null) {
288                 continue;
289             }
290             foreach (o; bucket) {
291                 if (o is null) {
292                     break;
293                 }
294                 a ~= o;
295             }
296         }
297         return a;
298     }
299 
300     public U[] toArray(U)(U[] a)
301     {
302 	if (a.length < size()) {
303             a = Arrays.copyOf(a, size());
304         }
305         int i = 0;
306         foreach (T[] bucket; buckets) {
307             if ( bucket==null ) {
308                 continue;
309             }
310             foreach (T o; bucket) {
311                 if (o is null) {
312                     break;
313                 }
314                 //@SuppressWarnings("unchecked") // array store will check this
315                 U targetElement = cast(U)o;
316                 a[i++] = targetElement;
317             }
318         }
319         return a;
320     }
321 
322     /**
323      * @uml
324      * @final
325      */
326     public final bool remove(T o)
327     {
328         return removeFast(o);
329     }
330 
331     public bool removeFast(T obj)
332     {
333 	if (obj is null) {
334             return false;
335         }
336         size_t b = getBucket(obj);
337         auto bucket = buckets[b];
338         if (bucket is null) {
339             // no bucket
340             return false;
341         }
342         for (int i=0; i<bucket.length; i++) {
343             auto e = bucket[i];
344             if (e is null) {
345                 // empty slot; not there
346                 return false;
347             }
348             if (opEqualsFp(e, obj) ) {  // found it
349                 // shift all elements to the right down one
350                 bucket.remove(i);
351                 bucket[$-1] = null;
352                 n--;
353                 return true;
354             }
355         }
356         return false;
357     }
358 
359     public bool containsAll(Object collection)
360     {
361 	if (collection.classinfo == Array2DHashSet!T.classinfo) {
362             Array2DHashSet!T s = to!(Array2DHashSet!T)(collection);
363             foreach (bucket; s.buckets) {
364                 if (bucket is null) continue;
365                 foreach (o; bucket) {
366                     if (o is null) break;
367                     if (!this.containsFast(o)) return false;
368                 }
369             }
370         }
371         else {
372             foreach (o; collection.tupleof) {
373                 if (!this.containsFast(o))
374                     return false;
375             }
376             return false;
377         }
378         return true;
379     }
380 
381     public bool addAll(T[] c)
382     {
383 	bool changed = false;
384         foreach (o; c) {
385             T existing = getOrAdd(o);
386             if (existing != o)
387                 changed = true;
388         }
389         return changed;
390     }
391 
392     public bool retainAll(T[] c)
393     {
394 	int newsize = 0;
395         foreach (bucket; buckets) {
396             if (bucket is null) {
397                 continue;
398             }
399             int i;
400             int j;
401             for (i = 0, j = 0; i < bucket.length; i++) {
402                 if (bucket[i] is null) {
403                     break;
404                 }
405                 auto bg = bucket[i];
406                 if (!c.canFind(bg)) {
407                     // removed
408                     continue;
409                 }
410                 // keep
411                 if (i != j) {
412                     bucket[j] = bucket[i];
413                 }
414                 j++;
415                 newsize++;
416             }
417             newsize += j;
418             while (j < i) {
419                 bucket[j] = null;
420                 j++;
421             }
422         }
423 
424         bool changed = newsize != n;
425         n = newsize;
426         return changed;
427 
428     }
429 
430     public bool removeAll(T[] c)
431     {
432         bool changed = false;
433         foreach (o; c) {
434             changed |= removeFast(o);
435         }
436         return changed;
437     }
438 
439     public void clear()
440     {
441 	buckets = createBuckets(INITAL_CAPACITY);
442         n = 0;
443         threshold = to!int(floor(INITAL_CAPACITY * LOAD_FACTOR));
444     }
445 
446     /**
447      * @uml
448      * @override
449      */
450     public override string toString()
451     {
452 	if (size == 0) return "{}";
453         auto buf = appender!string;
454         buf.put('{');
455         bool first = true;
456         foreach (bucket; buckets) {
457             if (bucket is null) continue;
458             foreach (o; bucket) {
459                 if (o is null) break;
460                 if ( first ) first=false;
461                 else buf.put(", ");
462                 buf.put(to!string(o));
463             }
464         }
465         buf.put('}');
466         return buf.data;
467     }
468 
469     public string toTableString()
470     {
471 	auto buf = appender!string;
472         foreach (bucket; buckets) {
473             if (bucket is null) {
474                 buf.put("null\n");
475                 continue;
476             }
477             buf.put('[');
478             bool first = true;
479             foreach (o; bucket) {
480                 if (first)
481                     first = false;
482                 else
483                     buf.put(" ");
484                 if (o is null) buf.put("_");
485                 else buf.put(to!string(o));
486             }
487             buf.put("]\n");
488         }
489         return buf.data;
490     }
491 
492     public T[][] createBuckets(int capacity)
493     {
494         T[][] obj;
495         obj.length = capacity;
496         debug {
497             writefln("T[][] obj -> %1$s, length -> %2$s", obj, obj.length);
498         }
499         return obj;
500     }
501 
502     public T[] createBucket(int capacity)
503     {
504         T[] obj;
505         obj.length = capacity;
506         return obj;
507     }
508 
509 }