introduce bitloops

This commit is contained in:
mike
2026-01-17 01:15:03 +01:00
parent 60b7509bf6
commit 4585c1f2eb
3 changed files with 146 additions and 120 deletions

View File

@@ -388,8 +388,8 @@ public class Main {
var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring); var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
if (mask == null) return null; if (mask == null) return null;
val multiThreaded = Thread.currentThread().getName().contains("pool");
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid(), multiThreaded);
TOTAL_NODES.addAndGet(filled.stats().nodes); TOTAL_NODES.addAndGet(filled.stats().nodes);
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks); TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);

View File

@@ -22,6 +22,8 @@ import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Locale; import java.util.Locale;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import static java.lang.Long.*;
import static java.lang.Long.numberOfTrailingZeros;
import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.US_ASCII;
/** /**
@@ -142,8 +144,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
stats.simplicity = k == 0 ? 0 : stats.simplicity / k; stats.simplicity = k == 0 ? 0 : stats.simplicity / k;
} }
} }
public int wordCount() { public int wordCount(int k) {
int k = 0;
for (var n = 1; n < clueMap.length; n++) if (clueMap[n] != X) k++; for (var n = 1; n < clueMap.length; n++) if (clueMap[n] != X) k++;
return k; return k;
} }
@@ -220,23 +221,23 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
public int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); } public int clueCount() { return bitCount(lo) + bitCount(hi); }
public double similarity(Clues b) { public double similarity(Clues b) {
long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo))); long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo)));
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi))); long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi)));
return (Long.bitCount(matchLo & MASK_LO) + Long.bitCount(matchHi & MASK_HI)) / SIZED; return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
} }
public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); } public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); }
public void forEachSlot(SlotVisitor visitor) { public void forEachSlot(SlotVisitor visitor) {
for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 1)); for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));
for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 0)); for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0));
for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 2)); for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2));
for (var l = lo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 3)); for (var l = lo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3));
for (var h = hi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | Long.numberOfTrailingZeros(h), 1)); for (var h = hi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1));
for (var h = hi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | Long.numberOfTrailingZeros(h), 0)); for (var h = hi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0));
for (var h = hi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | Long.numberOfTrailingZeros(h)), 2)); for (var h = hi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2));
for (var h = hi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | Long.numberOfTrailingZeros(h)), 3)); for (var h = hi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3));
} }
public Clues from(Clues best) { public Clues from(Clues best) {
lo = best.lo; lo = best.lo;
@@ -275,7 +276,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111); } static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111); }
static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); } static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); }
static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); } static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); }
static int length(long word) { return ((63 - Long.numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; } static int length(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; }
static ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]); static ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]);
public static String asWord(long word) { public static String asWord(long word) {
val len = Lemma.length(word); val len = Lemma.length(word);
@@ -338,7 +339,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
static final int BIT_FOR_DIR = 2; static final int BIT_FOR_DIR = 2;
static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); } static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
public static int length(long lo, long hi) { return Long.bitCount(lo) + Long.bitCount(hi); } public static int length(long lo, long hi) { return bitCount(lo) + bitCount(hi); }
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; } public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
public static int dir(int key) { return key & 3; } public static int dir(int key) { return key & 3; }
public static boolean increasing(int dir) { return (dir & 2) == 0; } public static boolean increasing(int dir) { return (dir & 2) == 0; }
@@ -357,12 +358,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long hitsHi = rayHi & c.hi; long hitsHi = rayHi & c.hi;
if (hitsHi != X) { if (hitsHi != X) {
int msb = 63 - Long.numberOfLeadingZeros(hitsHi); int msb = 63 - numberOfLeadingZeros(hitsHi);
long stop = 1L << msb; long stop = 1L << msb;
rayHi &= ~((stop << 1) - 1); // keep bits > stop rayHi &= ~((stop << 1) - 1); // keep bits > stop
rayLo = 0; // lo indices are below stop rayLo = 0; // lo indices are below stop
} else if (hitsLo != X) { } else if (hitsLo != X) {
int msb = 63 - Long.numberOfLeadingZeros(hitsLo); int msb = 63 - numberOfLeadingZeros(hitsLo);
long stop = 1L << msb; long stop = 1L << msb;
rayLo &= ~((stop << 1) - 1); rayLo &= ~((stop << 1) - 1);
} }
@@ -376,11 +377,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long hitsHi = rayHi & c.hi; long hitsHi = rayHi & c.hi;
if (hitsLo != X) { if (hitsLo != X) {
long stop = 1L << Long.numberOfTrailingZeros(hitsLo); long stop = 1L << numberOfTrailingZeros(hitsLo);
rayLo &= (stop - 1); rayLo &= (stop - 1);
rayHi = 0; // any hi is beyond the stop rayHi = 0; // any hi is beyond the stop
} else if (hitsHi != X) { } else if (hitsHi != X) {
long stop = 1L << Long.numberOfTrailingZeros(hitsHi); long stop = 1L << numberOfTrailingZeros(hitsHi);
// keep all lo (lo indices are < any hi index), but cut hi below stop // keep all lo (lo indices are < any hi index), but cut hi below stop
rayHi &= (stop - 1); rayHi &= (stop - 1);
} }
@@ -405,7 +406,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
for (long bits = lo_cl; bits != X; bits &= bits - 1) { for (long bits = lo_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits; long lsb = bits & -bits;
int clueIdx = Long.numberOfTrailingZeros(lsb); int clueIdx = numberOfTrailingZeros(lsb);
int v = (grid.vlo & lsb) != 0 ? 1 : 0; int v = (grid.vlo & lsb) != 0 ? 1 : 0;
int r = (grid.rlo & lsb) != 0 ? 1 : 0; int r = (grid.rlo & lsb) != 0 ? 1 : 0;
int key = Slot.packSlotKey(clueIdx, (r << 1) | v); int key = Slot.packSlotKey(clueIdx, (r << 1) | v);
@@ -413,16 +414,16 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long hLo = rLo & lo_cl, hHi = rHi & hi_cl; long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slot.increasing(key)) { if (Slot.increasing(key)) {
if (hLo != X) { if (hLo != X) {
rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1); rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0; rHi = 0;
} else if (hHi != X) { rHi &= ((1L << Long.numberOfTrailingZeros(hHi)) - 1); } } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); }
} else { } else {
if (hHi != X) { if (hHi != X) {
int msb = 63 - Long.numberOfLeadingZeros(hHi); int msb = 63 - numberOfLeadingZeros(hHi);
rHi &= ~((1L << msb << 1) - 1); rHi &= ~((1L << msb << 1) - 1);
rLo = 0; rLo = 0;
} else if (hLo != X) { } else if (hLo != X) {
int msb = 63 - Long.numberOfLeadingZeros(hLo); int msb = 63 - numberOfLeadingZeros(hLo);
rLo &= ~((1L << msb << 1) - 1); rLo &= ~((1L << msb << 1) - 1);
} }
} }
@@ -435,14 +436,14 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
cVLo |= rLo; cVLo |= rLo;
cVHi |= rHi; cVHi |= rHi;
} }
if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000; if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else { } else {
penalty += 25000; penalty += 25000;
} }
} }
for (long bits = hi_cl; bits != X; bits &= bits - 1) { for (long bits = hi_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits; long lsb = bits & -bits;
int clueIdx = Long.numberOfTrailingZeros(lsb); int clueIdx = numberOfTrailingZeros(lsb);
int v = (grid.vhi & lsb) != 0 ? 1 : 0; int v = (grid.vhi & lsb) != 0 ? 1 : 0;
int r = (grid.rhi & lsb) != 0 ? 1 : 0; int r = (grid.rhi & lsb) != 0 ? 1 : 0;
int key = Slot.packSlotKey(64 | clueIdx, (r << 1) | v); int key = Slot.packSlotKey(64 | clueIdx, (r << 1) | v);
@@ -450,16 +451,16 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long hLo = rLo & lo_cl, hHi = rHi & hi_cl; long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slot.increasing(key)) { if (Slot.increasing(key)) {
if (hLo != X) { if (hLo != X) {
rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1); rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0; rHi = 0;
} else if (hHi != X) { rHi &= ((1L << Long.numberOfTrailingZeros(hHi)) - 1); } } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); }
} else { } else {
if (hHi != X) { if (hHi != X) {
int msb = 63 - Long.numberOfLeadingZeros(hHi); int msb = 63 - numberOfLeadingZeros(hHi);
rHi &= ~((1L << msb << 1) - 1); rHi &= ~((1L << msb << 1) - 1);
rLo = 0; rLo = 0;
} else if (hLo != X) { } else if (hLo != X) {
int msb = 63 - Long.numberOfLeadingZeros(hLo); int msb = 63 - numberOfLeadingZeros(hLo);
rLo &= ~((1L << msb << 1) - 1); rLo &= ~((1L << msb << 1) - 1);
} }
} }
@@ -472,7 +473,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
cVLo |= rLo; cVLo |= rLo;
cVHi |= rHi; cVHi |= rHi;
} }
if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000; if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else { } else {
penalty += 25000; penalty += 25000;
} }
@@ -488,7 +489,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
// "unseen clues" in deze helft // "unseen clues" in deze helft
for (long bits = clueMask & ~seenMask, nLo, nHi; bits != X; bits &= bits - 1) { for (long bits = clueMask & ~seenMask, nLo, nHi; bits != X; bits &= bits - 1) {
int clueIdx = base | Long.numberOfTrailingZeros(bits); int clueIdx = base | numberOfTrailingZeros(bits);
// start nieuwe component // start nieuwe component
size = 0; size = 0;
@@ -515,7 +516,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
// push lo-neighbors // push lo-neighbors
while (nLo != X) { while (nLo != X) {
long lsb = nLo & -nLo; long lsb = nLo & -nLo;
int nidx = Long.numberOfTrailingZeros(nLo); // 0..63 int nidx = numberOfTrailingZeros(nLo); // 0..63
seenLo |= lsb; seenLo |= lsb;
stack[sp++] = nidx; stack[sp++] = nidx;
@@ -526,7 +527,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
// push hi-neighbors // push hi-neighbors
while (nHi != X) { while (nHi != X) {
long lsb = nHi & -nHi; long lsb = nHi & -nHi;
int nidx = 64 | Long.numberOfTrailingZeros(nHi); // 64..127 int nidx = 64 | numberOfTrailingZeros(nHi); // 64..127
seenHi |= lsb; seenHi |= lsb;
stack[sp++] = nidx; stack[sp++] = nidx;
@@ -540,9 +541,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
for (long bits = ~lo_cl; bits != X; bits &= bits - 1) { for (long bits = ~lo_cl; bits != X; bits &= bits - 1) {
int clueIdx = Long.numberOfTrailingZeros(bits); int clueIdx = numberOfTrailingZeros(bits);
var rci = IT[clueIdx]; var rci = IT[clueIdx];
if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
boolean h = (cHLo & (1L << clueIdx)) != X; boolean h = (cHLo & (1L << clueIdx)) != X;
boolean v = (cVLo & (1L << clueIdx)) != X; boolean v = (cVLo & (1L << clueIdx)) != X;
if (!h && !v) penalty += 1500; if (!h && !v) penalty += 1500;
@@ -550,9 +551,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
else penalty += 600; else penalty += 600;
} }
for (long bits = ~hi_cl & 0xFFL; bits != X; bits &= bits - 1) { for (long bits = ~hi_cl & 0xFFL; bits != X; bits &= bits - 1) {
int clueIdx = Long.numberOfTrailingZeros(bits); int clueIdx = numberOfTrailingZeros(bits);
var rci = IT[64 | clueIdx]; var rci = IT[64 | clueIdx];
if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
boolean h = (cHHi & (1L << clueIdx)) != X; boolean h = (cHHi & (1L << clueIdx)) != X;
boolean v = (cVHi & (1L << clueIdx)) != X; boolean v = (cVHi & (1L << clueIdx)) != X;
if (!h && !v) penalty += 1500; if (!h && !v) penalty += 1500;
@@ -629,14 +630,14 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
(a.rlo & ~maskLo) | (other.rlo & maskLo), (a.rlo & ~maskLo) | (other.rlo & maskLo),
(a.rhi & ~maskHi) | (other.rhi & maskHi)); (a.rhi & ~maskHi) | (other.rhi & maskHi));
for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 0); for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 0);
for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 1); for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 1);
for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 2); for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 2);
for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 3); for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 3);
for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, Long.numberOfTrailingZeros(h), 0); for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 0);
for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) clearCluesHi(c, Long.numberOfTrailingZeros(h), 1); for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 1);
for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (Long.numberOfTrailingZeros(h)), 2); for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 2);
for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (Long.numberOfTrailingZeros(h)), 3); for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 3);
return c; return c;
} }
public static void clearCluesLo(Clues out, int idx, int d) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d)])) out.clearClueLo(~(1L << idx)); } public static void clearCluesLo(Clues out, int idx, int d) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d)])) out.clearClueLo(~(1L << idx)); }
@@ -728,26 +729,26 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long p = 0; long p = 0;
if (Slot.increasing(key)) { if (Slot.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) { for (long b = lo & glo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
int i = Long.bitCount(lo & ((1L << idx) - 1)); int i = bitCount(lo & ((1L << idx) - 1));
p |= ((long) (i * 26 + g[idx])) << (i << 3); p |= ((long) (i * 26 + g[idx])) << (i << 3);
} }
int offset = Long.bitCount(lo); int offset = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1) { for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
int i = offset + Long.bitCount(hi & ((1L << idx) - 1)); int i = offset + bitCount(hi & ((1L << idx) - 1));
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
} }
} else { } else {
int offset = Long.bitCount(hi); int offset = bitCount(hi);
for (long b = hi & ghi; b != X; b &= b - 1) { for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))); int i = bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
} }
for (long b = lo & glo; b != X; b &= b - 1) { for (long b = lo & glo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))); int i = offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + g[idx])) << (i << 3); p |= ((long) (i * 26 + g[idx])) << (i << 3);
} }
} }
@@ -755,56 +756,56 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
static int slotScore(byte[] count, long lo, long hi) { static int slotScore(byte[] count, long lo, long hi) {
int cross = 0; int cross = 0;
for (long b = lo; b != X; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1); for (long b = lo; b != X; b &= b - 1) cross += (count[numberOfTrailingZeros(b)] - 1);
for (long b = hi; b != X; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); for (long b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
return cross * 10 + Slot.length(lo, hi); return cross * 10 + Slot.length(lo, hi);
} }
static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) { static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) {
final long glo = grid.lo, ghi = grid.hi; final long glo = grid.lo, ghi = grid.hi;
if (Slot.increasing(key)) { if (Slot.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) { for (long b = lo & glo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
if (g[idx] != Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1)))) return false; if (g[idx] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
} }
int bcLo = Long.bitCount(lo); int bcLo = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1) { for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
if (g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1)))) return false; if (g[64 | idx] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
} }
long maskLo = lo & ~glo, maskHi = hi & ~ghi; long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) { if ((maskLo | maskHi) != X) {
for (long b = maskLo; b != X; b &= b - 1) { for (long b = maskLo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
g[idx] = Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1))); g[idx] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
} }
for (long b = maskHi; b != X; b &= b - 1) { for (long b = maskHi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1))); g[64 | idx] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
} }
grid.lo |= maskLo; grid.lo |= maskLo;
grid.hi |= maskHi; grid.hi |= maskHi;
} }
} else { } else {
int bcHi = Long.bitCount(hi); int bcHi = bitCount(hi);
for (long b = hi & ghi; b != X; b &= b - 1) { for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
if (g[64 | idx] != Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; if (g[64 | idx] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
} }
for (long b = lo & glo; b != X; b &= b - 1) { for (long b = lo & glo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
if (g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; if (g[idx] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
} }
long maskLo = lo & ~glo, maskHi = hi & ~ghi; long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) { if ((maskLo | maskHi) != X) {
for (long b = maskHi; b != X; b &= b - 1) { for (long b = maskHi; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
g[64 | idx] = Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); g[64 | idx] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))));
} }
for (long b = maskLo; b != X; b &= b - 1) { for (long b = maskLo; b != X; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b); int idx = numberOfTrailingZeros(b);
g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); g[idx] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))));
} }
grid.lo |= maskLo; grid.lo |= maskLo;
grid.hi |= maskHi; grid.hi |= maskHi;
@@ -828,19 +829,19 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
p >>>= 8; p >>>= 8;
} else { } else {
p >>>= (Long.numberOfTrailingZeros(p) & ~7); p >>>= (numberOfTrailingZeros(p) & ~7);
} }
} }
int count = 0; int count = 0;
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]); for (int k = 0; k < numLongs; k++) count += bitCount(res[k]);
int[] indices = new int[count]; int[] indices = new int[count];
int ki = 0; int ki = 0;
for (int k = 0; k < numLongs; k++) { for (int k = 0; k < numLongs; k++) {
long w = res[k]; long w = res[k];
while (w != X) { while (w != X) {
int t = Long.numberOfTrailingZeros(w); int t = numberOfTrailingZeros(w);
indices[ki++] = (k << 6) | t; indices[ki++] = (k << 6) | t;
w &= w - 1; w &= w - 1;
} }
@@ -864,20 +865,20 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
p >>>= 8; p >>>= 8;
} else { } else {
p >>>= (Long.numberOfTrailingZeros(p) & ~7); p >>>= (numberOfTrailingZeros(p) & ~7);
} }
} }
int count = 0; int count = 0;
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]); for (int k = 0; k < numLongs; k++) count += bitCount(res[k]);
return count; return count;
} }
static void scoreSlots(int[] slotScores, Slot[] slots) { static void scoreSlots(int[] slotScores, Slot[] slots) {
val count = new byte[SIZE]; val count = new byte[SIZE];
for (var s : slots) { for (var s : slots) {
for (long b = s.lo; b != X; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++; for (long b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++;
for (long b = s.hi; b != X; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++; for (long b = s.hi; b != X; b &= b - 1) count[64 | numberOfTrailingZeros(b)]++;
} }
for (int i = 0; i < slots.length; i++) { for (int i = 0; i < slots.length; i++) {
var slot = slots[i]; var slot = slots[i];
@@ -885,8 +886,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
} }
public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask) { public static FillResult fillMask(final Rng rng,final Slot[] slots,final Grid mask, final boolean multiThreaded) {
val multiThreaded = Thread.currentThread().getName().contains("pool");
val NO_LOG = (!Main.VERBOSE || multiThreaded); val NO_LOG = (!Main.VERBOSE || multiThreaded);
val grid = mask; val grid = mask;
val used = new long[2048]; val used = new long[2048];
@@ -907,7 +907,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
long nodes; long nodes;
long backtracks; long backtracks;
int lastMRV; int lastMRV;
long lastLog = t0; long lastLog = t0, glo = grid.lo, ghi = grid.hi;
Pick current = CARRIER; Pick current = CARRIER;
void renderProgress() { void renderProgress() {
var now = System.currentTimeMillis(); var now = System.currentTimeMillis();
@@ -930,12 +930,44 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
System.out.print("\r" + Strings.padRight(msg, 120)); System.out.print("\r" + Strings.padRight(msg, 120));
System.out.flush(); System.out.flush();
} }
boolean placeWord(final int key, final long lo, final long hi, final long w) {
int idx;
if (Slot.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
int bcLo = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1)
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) {
for (long b = maskLo; b != X; b &= b - 1) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
glo |= maskLo;
ghi |= maskHi;
}
} else {
int bcHi = bitCount(hi);
for (long b = hi & ghi; b != X; b &= b - 1)
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
for (long b = lo & glo; b != X; b &= b - 1)
if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) {
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))));
for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))));
glo |= maskLo;
ghi |= maskHi;
}
}
return true;
}
void chooseMRV() { void chooseMRV() {
Slot best = null; Slot best = null;
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
var s = slots[i]; var s = slots[i];
if (assigned[s.key] != X) continue; if (assigned[s.key] != X) continue;
var pattern = patternForSlot(grid.lo, grid.hi, g, s.key, s.lo, s.hi); var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi);
var index = s.entry; var index = s.entry;
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
@@ -957,7 +989,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
current = PICK_DONE; current = PICK_DONE;
return; return;
} }
var pattern = patternForSlot(grid.lo, grid.hi, g, best.key, best.lo, best.hi); var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi);
var index = best.entry; var index = best.entry;
current = CARRIER; current = CARRIER;
current.slot = best; current.slot = best;
@@ -1004,9 +1036,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var w = entry.words[idx]; var w = entry.words[idx];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = grid.lo; low = glo;
top = grid.hi; top = ghi;
if (!placeWord(grid, g, k, slo, shi, w)) continue; if (!placeWord(k, slo, shi, w)) continue;
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
assigned[k] = w; assigned[k] = w;
@@ -1015,8 +1047,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
assigned[k] = X; assigned[k] = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);
grid.lo = low; glo = low;
grid.hi = top; ghi = top;
} }
backtracks++; backtracks++;
return false; return false;
@@ -1031,9 +1063,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var w = entry.words[idxInArray]; var w = entry.words[idxInArray];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = grid.lo; low = glo;
top = grid.hi; top = ghi;
if (!placeWord(grid, g, k, slo, shi, w)) continue; if (!placeWord(k, slo, shi, w)) continue;
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
assigned[k] = w; assigned[k] = w;
@@ -1042,8 +1074,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
assigned[k] = X; assigned[k] = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);
grid.lo = low; glo = low;
grid.hi = top; ghi = top;
} }
backtracks++; backtracks++;
@@ -1056,7 +1088,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
if (!NO_LOG) solver.renderProgress(); if (!NO_LOG) solver.renderProgress();
var ok = solver.backtrack(0); var ok = solver.backtrack(0);
// final progress line // final progress line
grid.lo = solver.glo;
grid.hi = solver.ghi;
var res = new FillResult(ok, new Gridded(grid), assigned, var res = new FillResult(ok, new Gridded(grid), assigned,
new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV)); new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
if (!multiThreaded) { if (!multiThreaded) {
@@ -1068,7 +1101,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
System.out.println( System.out.println(
String.format(Locale.ROOT, String.format(Locale.ROOT,
"[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs", "[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs",
res.wordCount(), TOTAL, res.nodes(), res.backtracks(), res.lastMRV(), res.seconds() res.wordCount(0), TOTAL, res.nodes(), res.backtracks(), res.lastMRV(), res.seconds()
) )
); );
} }

View File

@@ -180,9 +180,9 @@ public class MainTest {
128L, 128L,
422762372923520L, 422762372923520L,
192L); 192L);
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid(), false);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed"); Assertions.assertEquals(18, filled.wordCount(0), "Number of assigned words changed");
Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282])); Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282]));
Assertions.assertEquals(-1L, filled.grid().grid().lo); Assertions.assertEquals(-1L, filled.grid().grid().lo);
Assertions.assertEquals(255L, filled.grid().grid().hi); Assertions.assertEquals(255L, filled.grid().grid().hi);
@@ -201,7 +201,7 @@ public class MainTest {
foundSeed = seed; foundSeed = seed;
System.out.println("[DEBUG_LOG] Seed found: " + seed); System.out.println("[DEBUG_LOG] Seed found: " + seed);
System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().stats().simplicity); System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().stats().simplicity);
System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount()); System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount(0));
System.out.println("[DEBUG_LOG] Grid:"); System.out.println("[DEBUG_LOG] Grid:");
System.out.println(res.filled().grid().renderHuman(res.clues().c())); System.out.println(res.filled().grid().renderHuman(res.clues().c()));
System.out.println(res.filled().grid().gridToString(res.clues().c())); System.out.println(res.filled().grid().gridToString(res.clues().c()));
@@ -215,14 +215,7 @@ public class MainTest {
Assertions.assertEquals(12348, foundSeed, "Found seed changed"); Assertions.assertEquals(12348, foundSeed, "Found seed changed");
} }
boolean isLetter(byte b) { return (b & 64) != 0; } boolean isLetter(byte b) { return (b & 64) != 0; }
@Test @Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A')); }
public void testIsLetterA() { @Test public void testIsLetterZ() { assertTrue(isLetter((byte) 'Z')); }
assertTrue(isLetter((byte) 'A'));
}
@Test
public void testIsLetterZ() {
assertTrue(isLetter((byte) 'Z'));
}
} }