introduce bitloops

This commit is contained in:
mike
2026-01-12 06:21:52 +01:00
parent 1df6d6266d
commit b7b66b5cd6
2 changed files with 37 additions and 29 deletions

View File

@@ -245,30 +245,36 @@ public record SwedishGenerator(Rng rng) {
static record DictEntry(Lemma[] words, long[][] posBitsets) { }
public static record Lemma(int index, long word) {
public static record Lemma(long word) {
static int LEMMA_COUNTER = 0;
static long pack(String word) {
return pack(word.getBytes(US_ASCII));
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
static final long INDEX_MASK = (1L << 24) - 1; // 24 bits
static int LEMMA_COUNTER = 0;
static long pack(String word) { return pack(word.getBytes(US_ASCII)); }
static long pack(int index, byte[] b) {
return pack(b) | ((long) index << 40);
}
static long pack(byte[] b) {
long w = 0;
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & ~64) << (i * 5);
return w;
}
public Lemma(int index, String word) {
this(index, pack(word.getBytes(US_ASCII)));
}
public Lemma(int index, String word) { this(pack(index, word.getBytes(US_ASCII))); }
public Lemma(String word) { this(LEMMA_COUNTER++, word); }
byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
int intAt(int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; }
@Override public int hashCode() { return index; }
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.index == index); }
String[] clue() { return CsvIndexService.clues(index); }
int simpel() { return CsvIndexService.simpel(index); }
@Override public int hashCode() { return unpackIndex(word); }
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.word == word); }
String[] clue() { return CsvIndexService.clues(unpackIndex(word)); }
int simpel() { return CsvIndexService.simpel(unpackIndex(word)); }
int length() {
if (word == 0) return 0;
int highestBit = 63 - Long.numberOfLeadingZeros(word & 0xffffffffffffffffL);
int highestBit = 63 - Long.numberOfLeadingZeros(word & LETTER_MASK);
return (highestBit / 5) + 1;
}
static int usedCharsInPattern(long p) {
if (p == 0) return 0;
int highestBit = 63 - Long.numberOfLeadingZeros(p & LETTER_MASK); // 0-based
return (highestBit / 5) + 1;
}
public String asWord() {
@@ -276,6 +282,12 @@ public record SwedishGenerator(Rng rng) {
for (var i = 0; i < length(); i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
return new String(b, US_ASCII);
}
static int unpackIndex(long w) {
return (int) (w >>> 40);
}
static int unpackLetters(long w) {
return (int) (w & LETTER_MASK);
}
}
public static record Dict(
@@ -648,11 +660,7 @@ public record SwedishGenerator(Rng rng) {
//return pop.get(0).grid;
return best.grid;
}
static int usedCharsInPattern(long p) {
if (p == 0) return 0;
int highestBit = 63 - Long.numberOfLeadingZeros(p); // 0-based
return (highestBit / 5) + 1;
}
static long patternForSlot(Grid grid, Slot s) {
long p = 0;
for (int i = 0, len = s.len(); i < len; i++) {
@@ -702,7 +710,7 @@ public record SwedishGenerator(Rng rng) {
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = usedCharsInPattern(pattern); i < len; i++) {
for (int i = 0, len = Lemma.usedCharsInPattern(pattern); i < len; i++) {
int val = (int) ((pattern >>> (i * 5)) & 31);
if (val != 0) {
long[] bs = entry.posBitsets[i * 26 + (val - 1)];
@@ -742,7 +750,7 @@ public record SwedishGenerator(Rng rng) {
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = usedCharsInPattern(pattern); i < len; i++) {
for (int i = 0, len = Lemma.usedCharsInPattern(pattern); i < len; i++) {
int val = (int) ((pattern >>> (i * 5)) & 31);
if (val != 0) {
long[] bs = entry.posBitsets[i * 26 + (val - 1)];
@@ -872,18 +880,18 @@ public record SwedishGenerator(Rng rng) {
int idxInArray = (int) (r * r * r * L);
var idx = idxs[idxInArray];
var w = entry.words[idx];
if (used.get(w.index)) continue;
var lemIdx = Lemma.unpackIndex(w.word);
if (used.get(lemIdx)) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
used.set(w.index);
used.set(lemIdx);
assigned[k] = w;
if (backtrack(depth + 1)) return true;
assigned[k] = null;
used.clear(w.index);
used.clear(lemIdx);
s.undoPlace(grid, ctx.undo[depth]);
}
stats.backtracks++;
@@ -901,18 +909,18 @@ public record SwedishGenerator(Rng rng) {
double r = rng.nextFloat();
int idxInArray = (int) (r * r * r * N);
var w = entry.words[idxInArray];
if (used.get(w.index)) continue;
var lemIdx = Lemma.unpackIndex(w.word);
if (used.get(lemIdx)) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
used.set(w.index);
used.set(lemIdx);
assigned[k] = w;
if (backtrack(depth + 1)) return true;
assigned[k] = null;
used.clear(w.index);
used.clear(lemIdx);
s.undoPlace(grid, ctx.undo[depth]);
}

View File

@@ -107,7 +107,7 @@ public class SwedishGeneratorTest {
var l8a = new Lemma("INERENAE");
var l1 = new Lemma("APPLE");
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), l1.word());
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(l1.word()));
assertEquals(5, l1.length());
assertEquals((byte) 'A', l1.byteAt(0));
assertEquals(1, l1.intAt(0));
@@ -120,7 +120,7 @@ public class SwedishGeneratorTest {
var entry3 = dict.index()[3];
assertEquals(1, entry3.words().length);
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), entry3.words()[0].word());
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(entry3.words()[0].word()));
// Check pos indexing
// AXE: A at 0, X at 1, E at 2