///| A jump toggles exactly three bits. Their GF(2) span is a necessary, not sufficient,
///|
/// reachability condition. No claim of solvability follows from membership in this span.
fn Board::jump_basis(self : Board) -> Array[UInt64] {
let basis = Array::make(64, 0UL)
for j in self.jumps {
let mut v = bit(j.from) ^ bit(j.over) ^ bit(j.to)
for i = 63; i >= 0; i = i - 1 {
if (v & bit(i)) != 0UL {
if basis[i] == 0UL {
basis[i] = v
break
} else {
v = v ^ basis[i]
}
}
}
}
basis
}
///|
fn residue(bits : UInt64, basis : Array[UInt64]) -> UInt64 {
let mut v = bits
for i = 63; i >= 0; i = i - 1 {
if (v & bit(i)) != 0UL {
v = v ^ basis[i]
}
}
v
}
///|
pub fn Board::position_class(
self : Board,
p : Position,
) -> UInt64 raise PegError {
self.validate(p)
residue(p.bits, self.jump_basis())
}
///|
pub fn Board::class_compatible(
self : Board,
start : Position,
goal : Goal,
) -> Bool raise PegError {
self.validate(start)
self.validate_goal(goal)
let basis = self.jump_basis()
let sig = residue(start.bits, basis)
match goal {
Exact(p) => sig == residue(p.bits, basis)
AnySingle => {
for i = 0; i < self.size(); i = i + 1 {
if sig == residue(bit(i), basis) {
return true
}
}
false
}
}
}