introduce bitloops

This commit is contained in:
mike
2026-01-19 16:31:33 +01:00
parent 37581d15b4
commit 1fa112ab65
14 changed files with 393 additions and 455 deletions

View File

@@ -1,16 +1,10 @@
package puzzle;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;
import lombok.experimental.Delegate;
import lombok.val;
import precomp.Neighbors9x8;
import precomp.Neighbors9x8.rci;
import java.util.List;
import java.util.Locale;
import static java.lang.Long.*;
import static java.lang.Long.numberOfTrailingZeros;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -38,9 +32,7 @@ public record SwedishGenerator() {
public static final int LOG_EVERY_MS = 200;
public static final int BAR_LEN = 22;
public static final int C = Config.PUZZLE_COLS;
public static final double CROSS_R = (C - 1) / 2.0;
public static final int R = Config.PUZZLE_ROWS;
public static final double CROSS_C = (R - 1) / 2.0;
public static final int SIZE = C * R;// ~18
public static final int SIZE_MIN_1 = SIZE - 1;// ~18
public static final double SIZED = (double) SIZE;// ~18
@@ -55,7 +47,7 @@ public record SwedishGenerator() {
public static final byte DASH = (byte) C_DASH;
public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
public static final long RANGE_0_624 = 624L - 0L + 1L;
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
public static boolean isLo(int n) { return (n & 64) == 0; }
interface Bit1029 {
static long[] bit1029() { return new long[2048]; }
@@ -64,10 +56,7 @@ public record SwedishGenerator() {
static void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
}
static String padRight(String s, int n) {
if (s.length() >= n) return s;
return s + " ".repeat(n - s.length());
}
@AllArgsConstructor
public static class Pick {
@@ -76,29 +65,36 @@ public record SwedishGenerator() {
public int count;
}
public static final long[] OFFSETS_D_IDX=Neighbors9x8.OFFSETS_D_IDX;
public static final rci[] IT = Neighbors9x8.IT;
//@formatter:off
public static record Dict(DictEntry[] index, int length) { }
@AllArgsConstructor @NoArgsConstructor static class Assign { long w; }
public static final class FillStats { public double simplicity; }
//@formatter:on
@AllArgsConstructor
public static class Grid {
public final byte[] g;
public long lo, hi;
}
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX;
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
public static final long[] PATH_LO= Neighbors9x8.PATH_LO;
public static final long[] PATH_HI= Neighbors9x8.PATH_HI;
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
@RequiredArgsConstructor
public static final class FillStats {
public double simplicity;
}
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) {
}
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
public static final class Rng {
@Getter private int x;
private int x;
public Rng(int seed) {
var s = seed;
if (s == 0) s = 1;
@@ -114,9 +110,9 @@ public record SwedishGenerator() {
}
public byte randint2bitByte() {
int r = nextU32() & 7;
if (r < 4) return (byte) r;
if (r == 4) return (byte) 4;
return (byte) (r % 4);
//if (r < 4) return (byte) r;
return (byte) (r & 3);
}
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; }
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); }
@@ -127,20 +123,6 @@ public record SwedishGenerator() {
public int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
}
@AllArgsConstructor
public static class Grid {
public final byte[] g;
public long lo, hi;
public static int offset(int r, int c) { return r | (c << 3); }
public Grid copy() {
return new Grid(g.clone(), lo, hi);
}
}
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
public static interface Lemma {
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
@@ -148,28 +130,20 @@ public record SwedishGenerator() {
static long from(byte[] word) { return packShiftIn(word) | ((long) (word.length - 1) << 40); }
static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; }
/* static long pack(byte[] b) {
long w = 0;
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & 31) << (i * 5);
return w;
}*/
static long packShiftIn(byte[] b) {
long w = 0;
for (int i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31);
return w;
}
static public long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
static byte byteAt(long word, int idx) { return (byte) ((word >>> ((long) idx * 5L)) & 0b11111L); }
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111L); }
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
static ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]);
public static String asWord(long word) {
var b = BYTES.get();
public static String asWord(long word, byte[] bytes) {
int bi = 0;
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) {
b[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits
bytes[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits
}
return new String(b, 0, bi, US_ASCII);
return new String(bytes, 0, bi, US_ASCII);
}
static int unpackIndex(long w) { return (int) (w >>> 40); }
static int unpackShardIndex(long w) { return (int) (w >>> 43); }
@@ -177,15 +151,6 @@ public record SwedishGenerator() {
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
}
public static record Dict(DictEntry[] index, int length) { }
@AllArgsConstructor
@NoArgsConstructor
static class Assign {
long w;
}
public static record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry) {
public static int wordCount(int k, Slotinfo[] arr) {
@@ -203,34 +168,29 @@ public record SwedishGenerator() {
}
}
public static boolean isLo(int n) { return (n & 64) == 0; }
public static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) {
if (((lo & glo) | (hi & ghi)) == X) return 0;
if (((lo & glo) | (hi & ghi)) == X) return X;
long p = 0;
int n = 0;
int n = 0, offset, idx;
if (Slotinfo.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
int i = bitCount(lo & ((1L << idx) - 1));
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
idx = numberOfTrailingZeros(b);
p |= ((long) (bitCount(lo & ((1L << idx) - 1)) * 26 + g[idx])) << (n++ << 3);
}
int offset = bitCount(lo);
offset = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
int i = offset + bitCount(hi & ((1L << idx) - 1));
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
idx = numberOfTrailingZeros(b);
p |= ((long) ((offset + bitCount(hi & ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
}
} else {
int offset = bitCount(hi);
offset = bitCount(hi);
for (long b = hi & ghi; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
int i = bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
idx = numberOfTrailingZeros(b);
p |= ((long) (bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
}
for (long b = lo & glo; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
int i = offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
idx = numberOfTrailingZeros(b);
p |= ((long) ((offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))) * 26 + g[idx])) << (n++ << 3);
}
}
return p;
@@ -268,8 +228,7 @@ public record SwedishGenerator() {
}
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
final Grid grid,
final boolean NO_LOG) {
final Grid grid) {
val used = new long[2048];
val bitset = new long[2500];
val g = grid.g;
@@ -283,27 +242,6 @@ public record SwedishGenerator() {
int lastMRV;
long lastLog = t0, glo = grid.lo, ghi = grid.hi;
Pick current = CARRIER;
void renderProgress() {
var now = System.currentTimeMillis();
if ((now - lastLog) < LOG_EVERY_MS) return;
lastLog = (now);
var done = 0;
for (var lemma : slots) {
if (lemma.assign.w != X) done++;
}
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
var filled = Math.min(BAR_LEN, (int) Math.floor((pct / 100.0) * BAR_LEN));
var bar = "[" + "#".repeat(filled) + "-".repeat(BAR_LEN - filled) + "]";
var elapsed = String.format(Locale.ROOT, "%.1fs", (now - t0) / 1000.0);
var msg = String.format(
Locale.ROOT,
"%s %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %s",
bar, done, TOTAL, nodes, backtracks, lastMRV, elapsed
);
System.out.print("\r" + padRight(msg, 120));
System.out.flush();
}
boolean placeWordDec(final long lo, final long hi, final long w) {
int idx;
int bcHi = bitCount(hi);
@@ -358,7 +296,6 @@ public record SwedishGenerator() {
if (count <= 1) break;
}
}
// Re-calculate for the best slot to get actual indices
if (best == null) {
current = PICK_DONE;
return;
@@ -383,7 +320,6 @@ public record SwedishGenerator() {
}
val info = pick.indices;
lastMRV = pick.count;
if (!NO_LOG) renderProgress();
val s = pick.slot;
val inc = Slotinfo.increasing(s.key);
@@ -397,11 +333,8 @@ public record SwedishGenerator() {
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
for (var t = 0; t < tries; t++) {
//var r = rng.nextFloat();
//var idxInArray = (int) (r * r * r * (L - 1));
int idxInArray = rng.biasedIndexPow3(L - 1);
var w = entry.words[idxs[idxInArray/*(int) (r * r * r * (L - 1))*/]];
var lemIdx = Lemma.unpackIndex(w);
var w = entry.words[idxs[rng.biasedIndexPow3(L - 1)]];
var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue;
low = glo;
top = ghi;
@@ -427,8 +360,7 @@ public record SwedishGenerator() {
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
for (var t = 0; t < tries; t++) {
// double r = rng.nextFloat();
var w = entry.words[rng.biasedIndexPow3(N - 1)/*(int) (r * r * r * (N - 1))*/];
var w = entry.words[rng.biasedIndexPow3(N - 1)];
var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue;
low = glo;
@@ -454,11 +386,8 @@ public record SwedishGenerator() {
}
}
// initial render (same feel)
var solver = new Solver();
if (!NO_LOG) solver.renderProgress();
var ok = solver.backtrack(0);
// final progress line
var ok = solver.backtrack(0);
grid.lo = solver.glo;
grid.hi = solver.ghi;