jpayne@68
|
1 package bbmin;
|
jpayne@68
|
2
|
jpayne@68
|
3 import java.util.Arrays;
|
jpayne@68
|
4 import java.util.Random;
|
jpayne@68
|
5
|
jpayne@68
|
6 /**
|
jpayne@68
|
7 * @author Brian Bushnell
|
jpayne@68
|
8 * @date July 6, 2016
|
jpayne@68
|
9 *
|
jpayne@68
|
10 */
|
jpayne@68
|
11 public final class LongHashSet{
|
jpayne@68
|
12
|
jpayne@68
|
13 /*--------------------------------------------------------------*/
|
jpayne@68
|
14 /*---------------- Initialization ----------------*/
|
jpayne@68
|
15 /*--------------------------------------------------------------*/
|
jpayne@68
|
16
|
jpayne@68
|
17 public LongHashSet(){
|
jpayne@68
|
18 this(256);
|
jpayne@68
|
19 }
|
jpayne@68
|
20
|
jpayne@68
|
21 public LongHashSet(int initialSize){
|
jpayne@68
|
22 this(initialSize, 0.7f);
|
jpayne@68
|
23 }
|
jpayne@68
|
24
|
jpayne@68
|
25 public LongHashSet(int initialSize, float loadFactor_){
|
jpayne@68
|
26 invalid=randy.nextLong()|MINMASK;
|
jpayne@68
|
27 assert(invalid<0);
|
jpayne@68
|
28 assert(initialSize>0) : "Attempting to initialize a "+getClass().getSimpleName()+" of size<1.";
|
jpayne@68
|
29 assert(loadFactor_>0 && loadFactor_<1) : "Attempting to initialize a "+getClass().getSimpleName()+" with invalid load factor: "+loadFactor_;
|
jpayne@68
|
30 loadFactor=mid(0.25f, loadFactor_, 0.90f);
|
jpayne@68
|
31 resize(initialSize);
|
jpayne@68
|
32 }
|
jpayne@68
|
33
|
jpayne@68
|
34 /*--------------------------------------------------------------*/
|
jpayne@68
|
35 /*---------------- Public Methods ----------------*/
|
jpayne@68
|
36 /*--------------------------------------------------------------*/
|
jpayne@68
|
37
|
jpayne@68
|
38 public void clear(){
|
jpayne@68
|
39 if(size<1){return;}
|
jpayne@68
|
40 Arrays.fill(array, invalid);
|
jpayne@68
|
41 size=0;
|
jpayne@68
|
42 }
|
jpayne@68
|
43
|
jpayne@68
|
44 public boolean contains(long value){
|
jpayne@68
|
45 return value==invalid ? false : findCell(value)>=0;
|
jpayne@68
|
46 }
|
jpayne@68
|
47
|
jpayne@68
|
48 /**
|
jpayne@68
|
49 * Add this value to the set.
|
jpayne@68
|
50 * @param value
|
jpayne@68
|
51 * @return true if the value was added, false if it was already contained.
|
jpayne@68
|
52 */
|
jpayne@68
|
53 public boolean add(long value){
|
jpayne@68
|
54 if(value==invalid){resetInvalid();}
|
jpayne@68
|
55 int cell=findCellOrEmpty(value);
|
jpayne@68
|
56 if(array[cell]==invalid){
|
jpayne@68
|
57 array[cell]=value;
|
jpayne@68
|
58 size++;
|
jpayne@68
|
59 if(size>sizeLimit){resize();}
|
jpayne@68
|
60 return true;
|
jpayne@68
|
61 }
|
jpayne@68
|
62 assert(array[cell]==value);
|
jpayne@68
|
63 return false;
|
jpayne@68
|
64 }
|
jpayne@68
|
65
|
jpayne@68
|
66 /**
|
jpayne@68
|
67 * Remove this value from the set.
|
jpayne@68
|
68 * @param value
|
jpayne@68
|
69 * @return true if the value was removed, false if it was not present.
|
jpayne@68
|
70 */
|
jpayne@68
|
71 public boolean remove(long value){
|
jpayne@68
|
72 if(value==invalid){return false;}
|
jpayne@68
|
73 final int cell=findCell(value);
|
jpayne@68
|
74 if(cell<0){return false;}
|
jpayne@68
|
75 assert(array[cell]==value);
|
jpayne@68
|
76 array[cell]=invalid;
|
jpayne@68
|
77 size--;
|
jpayne@68
|
78
|
jpayne@68
|
79 rehashFrom(cell);
|
jpayne@68
|
80 return true;
|
jpayne@68
|
81 }
|
jpayne@68
|
82
|
jpayne@68
|
83 public int size(){return size;}
|
jpayne@68
|
84
|
jpayne@68
|
85 public boolean isEmpty(){return size==0;}
|
jpayne@68
|
86
|
jpayne@68
|
87 /*--------------------------------------------------------------*/
|
jpayne@68
|
88 /*---------------- String Methods ----------------*/
|
jpayne@68
|
89 /*--------------------------------------------------------------*/
|
jpayne@68
|
90
|
jpayne@68
|
91 @Override
|
jpayne@68
|
92 public String toString(){
|
jpayne@68
|
93 return toStringListView();
|
jpayne@68
|
94 }
|
jpayne@68
|
95
|
jpayne@68
|
96 public String toStringSetView(){
|
jpayne@68
|
97 StringBuilder sb=new StringBuilder();
|
jpayne@68
|
98 sb.append('[');
|
jpayne@68
|
99 String comma="";
|
jpayne@68
|
100 for(int i=0; i<array.length; i++){
|
jpayne@68
|
101 if(array[i]!=invalid){
|
jpayne@68
|
102 sb.append(comma+"("+i+", "+array[i]+")");
|
jpayne@68
|
103 comma=", ";
|
jpayne@68
|
104 }
|
jpayne@68
|
105 }
|
jpayne@68
|
106 sb.append(']');
|
jpayne@68
|
107 return sb.toString();
|
jpayne@68
|
108 }
|
jpayne@68
|
109
|
jpayne@68
|
110 public String toStringListView(){
|
jpayne@68
|
111 StringBuilder sb=new StringBuilder();
|
jpayne@68
|
112 sb.append('[');
|
jpayne@68
|
113 String comma="";
|
jpayne@68
|
114 for(int i=0; i<array.length; i++){
|
jpayne@68
|
115 if(array[i]!=invalid){
|
jpayne@68
|
116 sb.append(comma+array[i]);
|
jpayne@68
|
117 comma=", ";
|
jpayne@68
|
118 }
|
jpayne@68
|
119 }
|
jpayne@68
|
120 sb.append(']');
|
jpayne@68
|
121 return sb.toString();
|
jpayne@68
|
122 }
|
jpayne@68
|
123
|
jpayne@68
|
124 public long[] toArray(){
|
jpayne@68
|
125 long[] x=new long[array.length];
|
jpayne@68
|
126 int i=0;
|
jpayne@68
|
127 for(long v : array){
|
jpayne@68
|
128 x[i]=v;
|
jpayne@68
|
129 i++;
|
jpayne@68
|
130 }
|
jpayne@68
|
131 return x;
|
jpayne@68
|
132 }
|
jpayne@68
|
133
|
jpayne@68
|
134 /*--------------------------------------------------------------*/
|
jpayne@68
|
135 /*---------------- Private Methods ----------------*/
|
jpayne@68
|
136 /*--------------------------------------------------------------*/
|
jpayne@68
|
137
|
jpayne@68
|
138 public boolean verify(){
|
jpayne@68
|
139 int numValues=0;
|
jpayne@68
|
140 int numFound=0;
|
jpayne@68
|
141 for(int i=0; i<array.length; i++){
|
jpayne@68
|
142 final long value=array[i];
|
jpayne@68
|
143 if(value!=invalid){
|
jpayne@68
|
144 numValues++;
|
jpayne@68
|
145 final int cell=findCell(value);
|
jpayne@68
|
146 if(i==cell){
|
jpayne@68
|
147 numFound++;
|
jpayne@68
|
148 }else{
|
jpayne@68
|
149 return false;
|
jpayne@68
|
150 }
|
jpayne@68
|
151 }
|
jpayne@68
|
152 }
|
jpayne@68
|
153 return numValues==numFound && numValues==size;
|
jpayne@68
|
154 }
|
jpayne@68
|
155
|
jpayne@68
|
156 private void rehashFrom(int initial){
|
jpayne@68
|
157 if(size<1){return;}
|
jpayne@68
|
158 final int limit=array.length;
|
jpayne@68
|
159 for(int cell=initial+1; cell<limit; cell++){
|
jpayne@68
|
160 final long x=array[cell];
|
jpayne@68
|
161 if(x==invalid){return;}
|
jpayne@68
|
162 rehashCell(cell);
|
jpayne@68
|
163 }
|
jpayne@68
|
164 for(int cell=0; cell<initial; cell++){
|
jpayne@68
|
165 final long x=array[cell];
|
jpayne@68
|
166 if(x==invalid){return;}
|
jpayne@68
|
167 rehashCell(cell);
|
jpayne@68
|
168 }
|
jpayne@68
|
169 }
|
jpayne@68
|
170
|
jpayne@68
|
171 private boolean rehashCell(final int cell){
|
jpayne@68
|
172 final long value=array[cell];
|
jpayne@68
|
173 assert(value!=invalid);
|
jpayne@68
|
174 if(value==invalid){resetInvalid();}
|
jpayne@68
|
175 final int dest=findCellOrEmpty(value);
|
jpayne@68
|
176 if(cell==dest){return false;}
|
jpayne@68
|
177 assert(array[dest]==invalid);
|
jpayne@68
|
178 array[cell]=invalid;
|
jpayne@68
|
179 array[dest]=value;
|
jpayne@68
|
180 return true;
|
jpayne@68
|
181 }
|
jpayne@68
|
182
|
jpayne@68
|
183 private void resetInvalid(){
|
jpayne@68
|
184 final long old=invalid;
|
jpayne@68
|
185 long x=invalid;
|
jpayne@68
|
186 while(x==old || contains(x)){x=randy.nextLong()|MINMASK;}
|
jpayne@68
|
187 assert(x<0);
|
jpayne@68
|
188 invalid=x;
|
jpayne@68
|
189 for(int i=0; i<array.length; i++){
|
jpayne@68
|
190 if(array[i]==old){array[i]=invalid;}
|
jpayne@68
|
191 }
|
jpayne@68
|
192 }
|
jpayne@68
|
193
|
jpayne@68
|
194 private int findCell(final long value){
|
jpayne@68
|
195 if(value==invalid){return -1;}
|
jpayne@68
|
196
|
jpayne@68
|
197 final int limit=array.length, initial=(int)((value&MASK)%modulus);
|
jpayne@68
|
198 for(int cell=initial; cell<limit; cell++){
|
jpayne@68
|
199 final long x=array[cell];
|
jpayne@68
|
200 if(x==value){return cell;}
|
jpayne@68
|
201 if(x==invalid){return -1;}
|
jpayne@68
|
202 }
|
jpayne@68
|
203 for(int cell=0; cell<initial; cell++){
|
jpayne@68
|
204 final long x=array[cell];
|
jpayne@68
|
205 if(x==value){return cell;}
|
jpayne@68
|
206 if(x==invalid){return -1;}
|
jpayne@68
|
207 }
|
jpayne@68
|
208 return -1;
|
jpayne@68
|
209 }
|
jpayne@68
|
210
|
jpayne@68
|
211 private int findCellOrEmpty(final long value){
|
jpayne@68
|
212 assert(value!=invalid) : "Collision - this should have been intercepted.";
|
jpayne@68
|
213
|
jpayne@68
|
214 final int limit=array.length, initial=(int)((value&MASK)%modulus);
|
jpayne@68
|
215 for(int cell=initial; cell<limit; cell++){
|
jpayne@68
|
216 final long x=array[cell];
|
jpayne@68
|
217 if(x==value || x==invalid){return cell;}
|
jpayne@68
|
218 }
|
jpayne@68
|
219 for(int cell=0; cell<initial; cell++){
|
jpayne@68
|
220 final long x=array[cell];
|
jpayne@68
|
221 if(x==value || x==invalid){return cell;}
|
jpayne@68
|
222 }
|
jpayne@68
|
223 throw new RuntimeException("No empty cells - size="+size+", limit="+limit);
|
jpayne@68
|
224 }
|
jpayne@68
|
225
|
jpayne@68
|
226 public final void resizeDestructive(int newSize){
|
jpayne@68
|
227 size=0;
|
jpayne@68
|
228 sizeLimit=0;
|
jpayne@68
|
229 array=null;
|
jpayne@68
|
230 resize(newSize);
|
jpayne@68
|
231 }
|
jpayne@68
|
232
|
jpayne@68
|
233 private final void resize(){
|
jpayne@68
|
234 assert(size>=sizeLimit);
|
jpayne@68
|
235 resize(array.length*2L+1);
|
jpayne@68
|
236 }
|
jpayne@68
|
237
|
jpayne@68
|
238 private final void resize(final long size2){
|
jpayne@68
|
239 assert(size2>size) : size+", "+size2;
|
jpayne@68
|
240
|
jpayne@68
|
241 //This is supposed to be a prime but the primes code is ripped out in this version.
|
jpayne@68
|
242 //Any odd number is fine in most cases.
|
jpayne@68
|
243 long newPrime=size2|1;
|
jpayne@68
|
244 if(newPrime+extra>Integer.MAX_VALUE){
|
jpayne@68
|
245 newPrime=(Integer.MAX_VALUE-extra-2)|1;
|
jpayne@68
|
246 }
|
jpayne@68
|
247 assert(newPrime>modulus) : "Overflow: "+size+", "+size2+", "+modulus+", "+newPrime;
|
jpayne@68
|
248 modulus=(int)newPrime;
|
jpayne@68
|
249
|
jpayne@68
|
250 final int size3=(int)(newPrime+extra);
|
jpayne@68
|
251 sizeLimit=(int)(modulus*loadFactor);
|
jpayne@68
|
252 final long[] old=array;
|
jpayne@68
|
253 array=new long[size3];
|
jpayne@68
|
254 Arrays.fill(array, invalid);
|
jpayne@68
|
255
|
jpayne@68
|
256 // System.err.println("Resizing "+(old==null ? "null" : ""+old.length)+" to "+size3);
|
jpayne@68
|
257
|
jpayne@68
|
258 if(size<1){return;}
|
jpayne@68
|
259
|
jpayne@68
|
260 size=0;
|
jpayne@68
|
261 for(long value : old){
|
jpayne@68
|
262 if(value!=invalid){
|
jpayne@68
|
263 add(value);
|
jpayne@68
|
264 }
|
jpayne@68
|
265 }
|
jpayne@68
|
266 }
|
jpayne@68
|
267
|
jpayne@68
|
268 /*--------------------------------------------------------------*/
|
jpayne@68
|
269 /*---------------- Stuff From BBTools ----------------*/
|
jpayne@68
|
270 /*--------------------------------------------------------------*/
|
jpayne@68
|
271
|
jpayne@68
|
272 public static final float mid(float x, float y, float z){
|
jpayne@68
|
273 return x<y ? (x<z ? min(y, z) : x) : (y<z ? min(x, z) : y);
|
jpayne@68
|
274 }
|
jpayne@68
|
275 public static final float min(float x, float y){return x<y ? x : y;}
|
jpayne@68
|
276 public static final float max(float x, float y){return x>y ? x : y;}
|
jpayne@68
|
277
|
jpayne@68
|
278 /** Number of values that can be held without resizing */
|
jpayne@68
|
279 public int capacity(){
|
jpayne@68
|
280 return sizeLimit;
|
jpayne@68
|
281 }
|
jpayne@68
|
282
|
jpayne@68
|
283 /*--------------------------------------------------------------*/
|
jpayne@68
|
284 /*---------------- Fields ----------------*/
|
jpayne@68
|
285 /*--------------------------------------------------------------*/
|
jpayne@68
|
286
|
jpayne@68
|
287 private long[] array;
|
jpayne@68
|
288 private int size=0;
|
jpayne@68
|
289 /** Value for empty cells */
|
jpayne@68
|
290 private long invalid;
|
jpayne@68
|
291 private int modulus;
|
jpayne@68
|
292 private int sizeLimit;
|
jpayne@68
|
293 private final float loadFactor;
|
jpayne@68
|
294
|
jpayne@68
|
295 private static final Random randy=new Random(1);
|
jpayne@68
|
296 private static final long MASK=Long.MAX_VALUE;
|
jpayne@68
|
297 private static final long MINMASK=Long.MIN_VALUE;
|
jpayne@68
|
298
|
jpayne@68
|
299 private static final int extra=10;
|
jpayne@68
|
300
|
jpayne@68
|
301 }
|