Skip to content

rules bits

Eugene Lazutkin edited this page May 9, 2026 · 3 revisions

rules-bits

Bitwise rule library.

Import

import {rules as bitsRules} from 'yopl/rules/bits.js';

Predicates

bitAnd(X, Y, Z)X & Y = Z

Forward-only: requires X and Y to be bound; computes Z. With all three bound, behaves as a check. Cannot be solved backwards because bitwise AND is not invertible (many (X, Y) pairs produce the same Z).

const Z = variable('Z');
solve(bitsRules, 'bitAnd', [0b1100, 0b1010, Z], env => {
  console.log(assemble(Z, env).toString(2)); // 1000
});

bitOr(X, Y, Z)X | Y = Z

Forward (X and Y bound → Z computed; all three bound → check) plus zero-identity reverse mode: queries with X (or Y) bound to 0 resolve via (0, Y, Y) / (X, 0, X) fact clauses.

// 0 | Y = 5  →  Y = 5
const Y = variable('Y');
solve(bitsRules, 'bitOr', [0, Y, 5], env => console.log(assemble(Y, env))); // 5

// X | 0 = 5  →  X = 5
const X = variable('X');
solve(bitsRules, 'bitOr', [X, 0, 5], env => console.log(assemble(X, env))); // 5

Other reverse modes (e.g. X | Y = Z with neither operand at zero) are not enumerated — multiple (X, Y) pairs produce the same Z and the rule won't speculate. The forward path also cuts after success, so identity facts don't double-emit forward solutions like bitOr(0, 5, Z).

bitXor(X, Y, Z)X ^ Y = Z

Reversible. XOR is its own inverse (X ^ Y ^ Y = X), so any one missing operand can be solved for. Includes shortcut clauses 0 ^ Y = Y, X ^ 0 = X, X ^ X = 0.

// Solve for Y: 0b1100 ^ Y = 0b0110
const Y = variable('Y');
solve(bitsRules, 'bitXor', [0b1100, Y, 0b0110], env => {
  console.log(assemble(Y, env).toString(2)); // 1010
});

bitNot(X, Y)Y = ~X

Reversible bitwise NOT. Either argument may be unbound and is solved for; with both bound the rule behaves as a check.

Use cases

Bitwise predicates are most useful when integrating with code that already speaks bit flags — masking, packing, or parsing fixed-width records:

const rules = {
  ...systemRules,
  ...bitsRules,
  // hasFlag(Flags, Mask) — true if all bits in Mask are set in Flags
  hasFlag: (Flags, Mask) => [head(Flags, Mask), term('bitAnd', Flags, Mask, Mask)]
};

For pure-arithmetic work, prefer the rules-math library — bitwise tricks are clever but harder to read.

Limitations

  • Operands must be plain JavaScript numbers. The native &, |, ^, ~ operators coerce to 32-bit signed integers, so values outside that range are truncated.
  • bitAnd is not reversible — the AND identities don't usefully constrain a missing operand. bitOr reverses only via the zero-identity facts above (0 | Y = Y, X | 0 = X).

Clone this wiki locally