From b7b66b5cd66e49dc4ce2cde2738427db7509148e Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 12 Jan 2026 06:21:52 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/SwedishGenerator.java | 62 +++++++++++-------- .../java/puzzle/SwedishGeneratorTest.java | 4 +- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 318b791..9591b27 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -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]); } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 31e5895..c38b84a 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -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