introduce bitloops

This commit is contained in:
mike
2026-01-20 10:35:10 +01:00
parent 3cd17d170a
commit 5d0da1cf6b
2 changed files with 18 additions and 73 deletions

View File

@@ -704,64 +704,7 @@ public final class Masker {
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
}
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
public boolean isValid(int minLen) {
return findOffendingClue(minLen, new long[SwedishGenerator.SIZE], new long[SwedishGenerator.SIZE]) == -1;
}
public boolean isValid(int minLen, long[] slo, long[] shi) {
return findOffendingClue(minLen, slo, shi) == -1;
}
public int findOffendingClue(int minLen, long[] slo, long[] shi) {
if (((xlo & rlo) & lo) != X) return numberOfTrailingZeros((xlo & rlo) & lo);
if (((xhi & rhi) & hi) != X) return 64 | numberOfTrailingZeros((xhi & rhi) & hi);
int n = 0;
for (long bits = lo; bits != X; bits &= bits - 1) {
int idx = numberOfTrailingZeros(bits);
int dir = getDir(idx);
int key = Slot.packSlotKey(idx, dir);
long sLo = PATH_LO[key], sHi = PATH_HI[key];
long hLo = sLo & lo, hHi = sHi & hi;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
sLo &= (1L << numberOfTrailingZeros(hLo)) - 1;
sHi = 0;
} else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
} else {
if (hHi != X) {
sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1);
sLo = 0;
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
}
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
slo[n] = sLo;
shi[n] = sHi;
n++;
}
for (long bits = hi; bits != X; bits &= bits - 1) {
int idx = 64 | numberOfTrailingZeros(bits);
int dir = getDir(idx);
int key = Slot.packSlotKey(idx, dir);
long sLo = PATH_LO[key], sHi = PATH_HI[key];
long hLo = sLo & lo, hHi = sHi & hi;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
sLo &= (1L << numberOfTrailingZeros(hLo)) - 1;
sHi = 0;
} else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
} else {
if (hHi != X) {
sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1);
sLo = 0;
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
}
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
slo[n] = sLo;
shi[n] = sHi;
n++;
}
return -1;
}
public void forEachSlot(SlotVisitor visitor) {
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));
for (var l = lo & ~xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0));

View File

@@ -18,7 +18,7 @@ public class MaskerCluesTest {
for (int i = 0; i < 200; i++) {
for (int j = 19; j < 24; j++) {
var clues = masker.randomMask(j);
assertTrue(clues.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
assertTrue(masker.isValid(clues, MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
}
}
}
@@ -36,7 +36,7 @@ public class MaskerCluesTest {
simCount++;
masker.mutate(clues);
sim += orig.similarity(clues);
assertTrue(clues.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
assertTrue(masker.isValid(clues, MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
}
}
System.out.println("Average similarity: " + sim / simCount);
@@ -55,7 +55,7 @@ public class MaskerCluesTest {
simCount++;
var cross = masker.crossover(clues, clues2);
sim += Math.max(cross.similarity(clues), cross.similarity(clues2));
assertTrue(cross.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(cross).gridToString());
assertTrue(masker.isValid(cross, MIN_LEN), "Mask should be valid for length \n" + new Clued(cross).gridToString());
}
}
System.out.println("Average similarity: " + sim / simCount);
@@ -88,17 +88,18 @@ public class MaskerCluesTest {
@Test
void testIsValid() {
Clues g = Clues.createEmpty();
assertTrue(g.isValid(MIN_LEN));
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
Clues g = Clues.createEmpty();
assertTrue(masker.isValid(g, MIN_LEN));
// Valid clue: Right from (0,0) in 9x8 grid. Length is 8.
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
assertTrue(g.isValid(MIN_LEN));
assertTrue(masker.isValid(g, MIN_LEN));
// Invalid clue: Right from (0,7) in 9x8 grid. Length is 1 (too short if MIN_LEN >= 2).
Clues g2 = Clues.createEmpty();
g2.setClueLo(1L << Masker.offset(0, 7), (byte) 1);
assertFalse(g2.isValid(MIN_LEN));
assertFalse(masker.isValid(g2, MIN_LEN));
}
@Test
@@ -126,20 +127,20 @@ public class MaskerCluesTest {
@Test
void testIntersectionConstraint() {
Clues g = Clues.createEmpty();
Clues g = Clues.createEmpty();
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
// Clue 1: (0,0) Right. Slot cells: (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8)
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
// Clue 2: (1,2) Up. Slot cells: (0,2)
// Intersection is exactly 1 cell (0,2). Valid.
g.setClueLo(1L << Masker.offset(2, 2), (byte) 2);
assertTrue(g.isValid(MIN_LEN));
assertTrue(masker.isValid(g, MIN_LEN));
// Clue 3: (1,1) Right. Slot cells: (1,2), (1,3), ...
// No intersection with Clue 1 or 2. Valid.
g.setClueLo(1L << Masker.offset(1, 1), (byte) 1);
assertTrue(g.isValid(MIN_LEN));
assertTrue(masker.isValid(g, MIN_LEN));
// Now create a violation: two slots sharing 2 cells.
// We can do this with Corner Down and another clue.
@@ -150,19 +151,20 @@ public class MaskerCluesTest {
Clues g3 = Clues.createEmpty();
g3.setClueLo(1L << Masker.offset(0, 0), (byte) 4); // Corner Down
g3.setClueLo(1L << Masker.offset(0, 2), (byte) 5); // Corner Down Left
assertFalse(g3.isValid(MIN_LEN));
assertFalse(masker.isValid(g3, MIN_LEN));
}
@Test
void testInvalidDirectionBits() {
Clues g = Clues.createEmpty();
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
Clues g = Clues.createEmpty();
// Dir 6 (x=1, r=1, v=0) is invalid
g.setClueLo(1L << 0, (byte) 6);
assertFalse(g.isValid(MIN_LEN));
assertFalse(masker.isValid(g,MIN_LEN));
// Dir 7 (x=1, r=1, v=1) is invalid
Clues g2 = Clues.createEmpty();
g2.setClueLo(1L << 0, (byte) 7);
assertFalse(g2.isValid(MIN_LEN));
assertFalse(masker.isValid(g2,MIN_LEN));
}
}