1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
package structures;
import java.util.Arrays;
import shared.KillSwitch;
/**
* Like IntList2 but has a size for each array independent of its length.
* This is similar to a list of IntList but more efficient.
* Designed for use with HashArrayHybridFast.
* Assumes each entry is a set with all adds either ascending (Seal)
* or unique (Sketch).
*
* IntList3 does not extend IntList2 because virtual functions might be slower,
* but it would make the code more concise.
*
* @author Brian Bushnell
* @date Dec 10, 2018
*
*/
public final class IntList3{
/*--------------------------------------------------------------*/
/*---------------- Initialization ----------------*/
/*--------------------------------------------------------------*/
/** Re-call with default initial size and mode. */
public IntList3(){this(defaultInitialSize, defaultMode);}
/** Construct an IntList3 with this initial size.
* Setting the proper mode is the caller's responsibility. */
public IntList3(int initialSize, int mode_){
assert(initialSize>0);
entries=new int[initialSize][];
sizes=new int[initialSize];
mode=mode_;
assert(mode==ASCENDING || mode==UNIQUE) : "Unsupported mode: "+mode;
}
/*--------------------------------------------------------------*/
/*---------------- Mutation ----------------*/
/*--------------------------------------------------------------*/
/** Add this entry to the end of the list */
public final void add(int[] entry, int len){
set(size, entry, len);
}
/** Set this location to specified entry */
public final void set(int loc, int[] entry, int len){
assert((entry==null && len==0) || (entry.length>0 && len<=entry.length)) : len+", "+(entry==null ? entry : ""+entry.length);
if(loc>=entries.length){//Resize by doubling if necessary
resize(max(size*2, loc+1));
}
entries[loc]=entry;
sizes[loc]=len;
size=max(size, loc+1);
}
/**
* Add this value to the specified location.
* If an entry exists, insert the value, enlarging if necessary.
* Otherwise, create a new entry. */
public final int insertIntoList(final int v, final int loc){
//If the location is empty
if(loc>=size || entries[loc]==null){
//Add a new entry and return
set(loc, new int[] {v, INVALID}, 1);
return 1;
}
int[] entry=get(loc);
final int oldSize=sizes[loc];
//Scan the latest entries to see if v is already present.
for(int i=oldSize-1, lim=max(0, oldSize-slowAddLimit); i>=lim; i--){
if(entry[i]==v){return 0;}
assert(entry[i]<v || entry[i]!=v); //Ascending, the assumption for Seal; unique, the assumption for Sketch
assert(entry[i]!=INVALID);
}
//At this point the element was not found because it was not present or the size is too big
//If the entry is full, resize it
if(oldSize>=entry.length){
assert(oldSize==entry.length);
entry=KillSwitch.copyAndFill(entry, oldSize*2L, INVALID);
set(loc, entry, sizes[loc]);
}
//Quick add
assert(entry[oldSize]==INVALID);
entry[oldSize]=v;
sizes[loc]++;
return 1;
}
/*--------------------------------------------------------------*/
/*---------------- Resizing ----------------*/
/*--------------------------------------------------------------*/
/** Resize the array of entries */
public final void resize(int size2){
assert(size2>size);
entries=KillSwitch.copyOf(entries, size2);//TODO: Safe copy to prevent memory exception
sizes=KillSwitch.copyOf(sizes, size2);
}
/** Compress the list by eliminating the unused trailing space */
public final void shrink(){
if(size==entries.length){return;}
entries=KillSwitch.copyOf(entries, size);
sizes=KillSwitch.copyOf(sizes, size);
}
/**
* Provided for supporting disordered additions; not currently used.
* Make each entry unique and minimal-length.
* */
public final void shrinkToUnique(){
assert(false) : "TODO: This function has not been tested and is not expected to be used.";
assert(!shrunk);
assert(mode==DISORDERED);//Not really necessary
for(int i=0; i<size; i++){
if(sizes[i]>slowAddLimit){//Under this limit everything is already unique
shrinkToUnique(i);
}
}
shrunk=true;
assert(readyToUse());
}
/**
* Provided for supporting disordered additions; not currently used.
* I could redo this so that the sets become packed and the size changes without creating a new array.
* */
private void shrinkToUnique(int loc){
final int[] entry=entries[loc];
final int oldSize=sizes[loc];
assert(oldSize>1);
Arrays.sort(entry, 0, oldSize);
int unique=0;
int prev=INVALID;
for(int i=0; i<oldSize; i++){
assert(entry[i]>=0);
int current=entry[i];
if(current!=prev){
unique++;
}
prev=current;
}
if(unique==oldSize){return;} //No need to condense aside from saving a little RAM
int[] set2=new int[unique];
unique=0;
prev=INVALID;
for(int i=0; i<oldSize; i++){
int current=entry[i];
if(current!=prev){
set2[unique]=current;
unique++;
}
prev=current;
}
set(loc, set2, unique);
}
private boolean readyToUse(){
return shrunk || mode==ASCENDING || mode==UNIQUE;
}
/*--------------------------------------------------------------*/
/*---------------- Reading ----------------*/
/*--------------------------------------------------------------*/
/** Get the entry at the location */
public final int[] get(int loc){
return(loc>=size ? null : entries[loc]);
}
/** Added for better IntList2 compatibility */
public int getLen(int i) {
return i>=size ? 0 : sizes[i];
}
/*--------------------------------------------------------------*/
/*---------------- ToString ----------------*/
/*--------------------------------------------------------------*/
@Override
public String toString(){
StringBuilder sb=new StringBuilder();
sb.append('[');
String comma="";
for(int i=0; i<size; i++){
if(entries[i]!=null){//Could be improved to use sizes
sb.append(comma+"("+i+", "+Arrays.toString(entries[i])+")");
comma=", ";
}
}
sb.append(']');
return sb.toString();
}
/*--------------------------------------------------------------*/
/*---------------- Static Methods ----------------*/
/*--------------------------------------------------------------*/
private static final int min(int x, int y){return x<y ? x : y;}
private static final int max(int x, int y){return x>y ? x : y;}
/*--------------------------------------------------------------*/
/*---------------- Fields ----------------*/
/*--------------------------------------------------------------*/
/** Holds entries. Each entry is a sets of numbers in an int[].
* Leftmost values are valid, rightmost values are invalid. */
private int[][] entries;
/** Number of values in each entry. */
private int[] sizes;
/** Number of entries in the primary array. */
public int size=0;
/** True after shrinkToUnique has been called.
* Currently unused. */
private boolean shrunk=false;
/** Preconditions for adding values */
private final int mode;
/*--------------------------------------------------------------*/
/*---------------- Static Fields ----------------*/
/*--------------------------------------------------------------*/
/** This could be made mutable per instance, but it would be a lot of work. */
public static final int INVALID=-1;
/** Only scan back this far for duplicates when adding values */
public static final int slowAddLimit=4;
/*--------------------------------------------------------------*/
/*---------------- Modes ----------------*/
/*--------------------------------------------------------------*/
/** All adds to an entry must be nondescending */
public static final int ASCENDING=1;
/** All adds to an entry must be unique */
public static final int UNIQUE=2;
/** No requirements for adds.
* To ensure set functionality, shrinkToUnique should be called before use. */
public static final int DISORDERED=3;
/** Should be set prior to creation, e.g. by Seal or Sketch */
public static int defaultMode=ASCENDING;
public static int defaultInitialSize=256;
}
|