Skip to content

Commit 8a19e73

Browse files
committed
feat: can parse any byte shifts array for indexed tables
1 parent a5f9dcb commit 8a19e73

2 files changed

Lines changed: 145 additions & 88 deletions

File tree

common-tools/clas-utils/src/main/java/org/jlab/utils/groups/IndexedList.java

Lines changed: 136 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,224 +4,272 @@
44
import java.util.Map;
55

66
/**
7-
* A generic class representing a collection of elements identified
8-
* by a series of indices which length can vary.
9-
* The indices are hashed into a single long key indexing the collection.
7+
* A generic class representing a collection of elements identified by a series
8+
* of indices which length can vary. The indices are hashed into a single long
9+
* key indexing the collection.
1010
*
1111
* @param <T> the type of elements stored in the list
1212
*
1313
* @author gavalian
1414
*/
1515
public class IndexedList<T> {
16+
1617
//Collection of elements
17-
private final Map<Long,T> collection = new LinkedHashMap<>();
18+
private final Map<Long, T> collection = new LinkedHashMap<>();
1819
//Number of indices
1920
private int indexSize;
2021
//index generator used for hashing the multiple
2122
//indices into a single long key
2223
private IndexGenerator indexGenerator;
23-
24+
2425
/**
2526
* Constructs an empty IndexedList with the default index size (3).
2627
*/
2728
public IndexedList() {
28-
this(3);
29+
this.indexSize = 3;
30+
//Initialization with the default index
31+
this.indexGenerator = new IndexGenerator();
2932
}
30-
33+
3134
/**
3235
* Constructs an IndexedList with the specified number of indices.
3336
*
3437
* @param indsize the number of indices
3538
*/
36-
public IndexedList(int indsize){
39+
public IndexedList(int indsize) {
3740
this.indexSize = indsize;
41+
//Initialization with the default index
42+
this.indexGenerator = new IndexGenerator();
43+
}
44+
45+
/**
46+
* Constructs an IndexedList with the specified number of indices.
47+
*
48+
* @param byteShifts the byte shifts to consider to build the index
49+
*/
50+
public IndexedList(int[] byteShifts) {
51+
this.indexSize = byteShifts.length;
3852
//Initialization with the index size
39-
this.indexGenerator = new IndexGenerator(indsize);
53+
this.indexGenerator = new IndexGenerator(byteShifts);
4054
}
41-
42-
55+
4356
/**
44-
* Helper method to check if an index can be looked up.
45-
*
46-
* @param index array to check
47-
* @return true or false if the index is valid or not
48-
*/
57+
* Helper method to check if an index can be looked up.
58+
*
59+
* @param index array to check
60+
* @return true or false if the index is valid or not
61+
*/
4962
private boolean isValidIndex(int... index) {
50-
return index != null && index.length == this.indexSize;
63+
return index != null && index.length == this.indexSize;
5164
}
52-
65+
5366
/**
54-
* Helper method to handle errors for wrong indices
55-
*
56-
* @param index array to check
57-
*/
67+
* Helper method to handle errors for wrong indices
68+
*
69+
* @param index array to check
70+
* @throws IllegalArgumentException if the number of indices or their values
71+
* exceeds supported parameters
72+
*/
5873
private void validateIndex(int... index) {
59-
if (!isValidIndex(index)) {
60-
throw new IllegalArgumentException("Index length mismatch: expected " + this.indexSize);
61-
}
74+
if (!isValidIndex(index)) {
75+
throw new IllegalArgumentException("Index length mismatch: expected " + this.indexSize);
76+
}
77+
for (int i = 0; i < index.length-1; i++) {
78+
int bits = (i != 0)
79+
? indexGenerator.getByteShifts()[i] - indexGenerator.getByteShifts()[i + 1]
80+
: 64 - indexGenerator.getByteShifts()[i]; // First field: number of bits from shift to 64
6281

63-
int maxValue = (indexGenerator.shiftsToConsider == IndexGenerator.BYTE_SHIFTS) ? 0xFFFF : 0x7F;
82+
int maxValue = (1 << bits) - 1;
6483

65-
for (int i : index) {
66-
if (i < 0 || i > maxValue) {
67-
throw new IllegalArgumentException(
68-
String.format("Index value out of range (0–%d): %d", maxValue, i)
69-
);
84+
// Check if the index value is within the allowed range
85+
if (index[i] < 0 || index[i] > maxValue) {
86+
throw new IllegalArgumentException(
87+
String.format("Index value out of range (0–%d) for byte shift %d: %d", maxValue, bits, index[i])
88+
);
89+
}
7090
}
7191
}
72-
}
73-
92+
7493
/**
7594
* Adds an item to the collection with its index.
7695
*
7796
* @param item the item to be added
7897
* @param index the index array used to identify the item
7998
*/
80-
public void add(T item, int... index){
99+
public void add(T item, int... index) {
81100
validateIndex(index);
82101
long code = this.indexGenerator.hashCode(index);
83102
this.collection.put(code, item);
84103
}
85-
104+
86105
/**
87106
* Checks whether an item exists for the specified index.
88107
*
89108
* @param index the index to look up
90109
* @return true if an item exists at the index; false otherwise
91110
*/
92-
public boolean hasItem(int... index){
93-
if(!isValidIndex(index)) return false;
111+
public boolean hasItem(int... index) {
112+
if (!isValidIndex(index)) {
113+
return false;
114+
}
94115
long code = indexGenerator.hashCode(index);
95116
return this.collection.containsKey(code);
96117
}
97-
118+
98119
/**
99120
* Retrieves an item by its index.
100121
*
101122
* @param index the index to find
102123
* @return the item at the index, null if not found
103124
*/
104-
public T getItem(int... index){
105-
if (!isValidIndex(index)) return null;
125+
public T getItem(int... index) {
126+
if (!isValidIndex(index)) {
127+
return null;
128+
}
106129
long code = indexGenerator.hashCode(index);
107130
return this.collection.get(code);
108131
}
109-
132+
110133
/**
111134
* Clears items from the collection.
112135
*/
113-
public void clear(){this.collection.clear();}
136+
public void clear() {
137+
this.collection.clear();
138+
}
139+
114140
/**
115141
* Gets the number of indices used to identify elements.
116142
*
117143
* @return the index size
118144
*/
119-
public int getIndexSize(){ return this.indexSize;}
145+
public int getIndexSize() {
146+
return this.indexSize;
147+
}
148+
120149
/**
121150
* Returns the collection of items.
122151
*
123152
* @return the map of hashed keys to items
124153
*/
125-
public Map<Long,T> getMap(){ return this.collection;}
154+
public Map<Long, T> getMap() {
155+
return this.collection;
156+
}
157+
126158
/**
127159
* Returns the index generator for this collection.
128160
*
129161
* @return the index generator
130162
*/
131-
public IndexGenerator getIndexGenerator(){ return this.indexGenerator;}
132-
163+
public IndexGenerator getIndexGenerator() {
164+
return this.indexGenerator;
165+
}
166+
167+
/**
168+
* Sets the index generator for this collection.
169+
*
170+
* @param indexGenerator, the {
171+
* @IndexGenerator} to be set
172+
*/
173+
public void setIndexGenerator(IndexGenerator indexGenerator) {
174+
this.indexGenerator = indexGenerator;
175+
}
176+
133177
/**
134178
* Displays the collection
135179
*/
136-
public void show(){
137-
for(Map.Entry<Long,T> entry : this.collection.entrySet()){
180+
public void show() {
181+
for (Map.Entry<Long, T> entry : this.collection.entrySet()) {
138182
String indexString = indexGenerator.getString(entry.getKey(), this.indexSize);
139-
System.out.println(String.format("[%s] : ",
183+
System.out.println(String.format("[%s] : ",
140184
indexString) + entry.getValue());
141185
}
142186
}
143187

144188
/**
145-
* Utility class for generating and decoding a long key from a multi-dimensional index.
146-
* either up to 4 indices with 16 bits, or up to 9 indices with 7 bits (int up to 128) are handled
189+
* Utility class for generating and decoding a long key from a
190+
* multi-dimensional index. Default is up to 4 indices with 16 bits, but any
191+
* byte shifts can be set up to a max of 64
147192
*/
148193
public static class IndexGenerator {
149-
150-
//Nominal case, only works up to four indices. 4 times 16 bits = 64 bits for a long
151-
static int[] BYTE_SHIFTS = new int[]{48,32,16,0};
152-
//Case where you can use up to 9 indices that are int up to 128
153-
//2^7 = 128 and 9*7 = 63 < 64
154-
static int[] COMPRESSED_SHIFTS = new int[]{56, 49, 42, 35, 28, 21, 14, 7, 0};
155-
//This is the shifts that will be considered for all computations
156-
private int[] shiftsToConsider;
157-
194+
195+
private int[] byteShifts = new int[]{48, 32, 16, 0};
196+
158197
/**
159198
* Constructs an IndexGenerator with generic index size.
160-
*
161199
*/
162-
public IndexGenerator(){
163-
this(3);
200+
public IndexGenerator() {
201+
//Nominal case, only works up to four indices. 4 times 16 bits = 64 bits for a long
202+
this.byteShifts = new int[]{48, 32, 16, 0};
164203
}
165-
204+
166205
/**
167-
* Constructs an IndexGenerator for the specified index size.
206+
* Constructs an IndexGenerator from a given byte shifts array.
168207
*
169-
* @param indsize the number of indices to encode
170-
* @throws IllegalArgumentException if the number of indices exceeds the supported limit
208+
* @param byteShifts the array of byte shifts to consider
171209
*/
172-
public IndexGenerator(int indsize){
173-
if (indsize > this.COMPRESSED_SHIFTS.length) {
174-
throw new IllegalArgumentException("# indices is larger than "+ this.COMPRESSED_SHIFTS.length);
175-
}
176-
else if (indsize > this.BYTE_SHIFTS.length) {
177-
this.shiftsToConsider = this.COMPRESSED_SHIFTS;
210+
public IndexGenerator(int[] byteShifts) {
211+
// Check that no byte shift exceeds 64
212+
for (int shift : byteShifts) {
213+
if (shift < 0 || shift >= 64) {
214+
throw new IllegalArgumentException("Byte shift must be between 0 and 63.");
215+
}
178216
}
179-
else this.shiftsToConsider = this.BYTE_SHIFTS;
217+
this.byteShifts = byteShifts;
218+
}
219+
220+
/**
221+
* Get the byte shifts
222+
*
223+
* @return the array of byte shifts
224+
*/
225+
public int[] getByteShifts() {
226+
return this.byteShifts;
180227
}
181228

182229
/**
183230
* Generates a long key from the given array of indices.
184231
*
185232
* @param indices the index array
186233
* @return a long key representing the hashed index
187-
* @throws IllegalArgumentException if the number of indices exceeds supported length
234+
* @throws IllegalArgumentException if the number of indices exceeds
235+
* supported length
188236
*/
189-
public long hashCode(int... indices){
237+
public long hashCode(int... indices) {
190238
long result = (long) 0;
191-
192-
if (indices.length > this.shiftsToConsider.length) {
193-
throw new IllegalArgumentException("# indices is larger than "+ this.shiftsToConsider.length);
239+
240+
if (indices.length > this.byteShifts.length) {
241+
throw new IllegalArgumentException("# indices is larger than " + this.byteShifts.length);
194242
}
195-
196-
for(int loop = 0; loop < indices.length; loop++){
197-
long patern = (((long) indices[loop])&0x000000000000FFFF)<<this.shiftsToConsider[loop];
243+
244+
for (int loop = 0; loop < indices.length; loop++) {
245+
long patern = (((long) indices[loop]) & 0x000000000000FFFF) << this.byteShifts[loop];
198246
result = (result | patern);
199247
}
200248
return result;
201249
}
202-
250+
203251
/**
204252
* Retrieves a specific index from the encoded long key.
205253
*
206254
* @param hashcode the encoded long key
207255
* @param order the position of the index to retrieve
208256
* @return the decoded index
209257
*/
210-
public int getIndex(long hashcode, int order){
211-
int result = (int) (hashcode>>this.shiftsToConsider[order])&0x000000000000FFFF;
258+
public int getIndex(long hashcode, int order) {
259+
int result = (int) (hashcode >> this.byteShifts[order]) & 0x000000000000FFFF;
212260
return result;
213261
}
214-
262+
215263
/**
216264
* Returns a formatted string representing all indices in the hash key.
217265
*
218266
* @param hashcode the encoded long key
219267
* @param length the number of indices to extract
220268
* @return a string representation of the indices
221269
*/
222-
public String getString(long hashcode, int length){
270+
public String getString(long hashcode, int length) {
223271
StringBuilder str = new StringBuilder();
224-
for(int loop = 0; loop <length; loop++){
272+
for (int loop = 0; loop < length; loop++) {
225273
str.append(String.format("%5d", this.getIndex(hashcode, loop)));
226274
}
227275
return str.toString();

common-tools/clas-utils/src/main/java/org/jlab/utils/groups/IndexedTable.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import javax.swing.JTable;
1313
import javax.swing.table.DefaultTableCellRenderer;
1414
import javax.swing.table.DefaultTableModel;
15+
import org.jlab.utils.groups.IndexedList.IndexGenerator;
1516

1617
/**
1718
*
@@ -58,6 +59,14 @@ public IndexedTable(int indexCount,String[] format){
5859
}
5960
}
6061

62+
public void setByteShifts(int[] byteShifts){
63+
this.entries.setIndexGenerator(new IndexGenerator(byteShifts));
64+
}
65+
66+
public int[] getByteShifts(){
67+
return this.entries.getIndexGenerator().getByteShifts();
68+
}
69+
6170
public void setPrecision(Integer precision){
6271
StringBuilder str = new StringBuilder();
6372
str.append("%.");

0 commit comments

Comments
 (0)