UNPKG

espresso-iisojs

Version:

Implementation of Espresso-II method for heuristic minimization of single output boolean functions

1,662 lines (1,650 loc) 45.3 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/main.ts var main_exports = {}; __export(main_exports, { allSat: () => allSat2, complement: () => complement2, espresso: () => espresso2, sat: () => sat2, tautology: () => tautology2 }); module.exports = __toCommonJS(main_exports); // src/bigint.ts function and(a, b) { return a & b; } function or(a, b) { return a | b; } function xor(a, b) { return a ^ b; } function not(a) { return ~a; } function lshift(a, b) { return a << b; } function rshift(a, b) { return a >> b; } function sub(a, b) { return a - b; } function toNumber(a) { return Number(a); } function eq(a, b) { return a === b; } function ne(a, b) { return a !== b; } function gte(a, b) { return a >= b; } function asIntN(a, b) { return BigInt.asIntN(a, b); } var _BigInt = BigInt; // src/common.ts var MAX_LITERALS = 512; var BIGINT_0 = _BigInt(0); var BIGINT_1 = _BigInt(1); var BIGINT_0C = not(BIGINT_0); var BIGINT_32 = _BigInt(32); var BIGINT_INDEX = []; var BIGINT_INDEXC = []; for (let i = 0; i <= MAX_LITERALS; ++i) { const b = lshift(BIGINT_1, _BigInt(i)); BIGINT_INDEX.push(b); BIGINT_INDEXC.push(not(b)); } var BIGINT_ODD = BIGINT_INDEX.filter((b, i) => i % 2 === 0).reduce( (acc, cur) => or(acc, cur) ); function bitIndices(bits) { const res = []; let offset = 0; while (ne(bits, BIGINT_0)) { let b = toNumber(asIntN(32, bits)); bits = rshift(bits, BIGINT_32); let i = Math.clz32(b); while (i < 32) { const li = 31 - i; res.push(offset + li); b ^= 1 << li; i = Math.clz32(b); } offset += 32; } return res; } function bitIndicesOdd(bits) { const ODD = 1431655765; const res = []; let offset = 0; while (ne(bits, BIGINT_0)) { let b = toNumber(asIntN(32, bits)); b = b & ODD | b >> 1 & ODD; bits = rshift(bits, BIGINT_32); let i = Math.clz32(b); while (i < 32) { const li = 31 - i; res.push(offset + li); b ^= 1 << li; i = Math.clz32(b); } offset += 32; } return res; } function componentReduction(cubes, cols) { const map = /* @__PURE__ */ new Map(); for (const i of cols) { const I = i - i % 2; if (map.has(I)) continue; const s = /* @__PURE__ */ new Set(); for (const c of cubes) if (c.set.has(i) || c.set.has(i + 1)) s.add(c); if (s.size) map.set(I, s); } if (map.size <= 1) return [cubes]; for (const c of cubes) { let prev = -1; for (const [k, v] of map) { if (!v.has(c)) continue; if (prev === -1) { prev = k; continue; } map.set(prev, /* @__PURE__ */ new Set([...map.get(prev), ...v])); map.delete(k); if (map.size === 1) return [cubes]; } } return [...map.values()].map((c) => [...c]); } function invBi(n) { return or( lshift(and(n, BIGINT_ODD), BIGINT_1), and(rshift(n, BIGINT_1), BIGINT_ODD) ); } function popcount(n, max = MAX_LITERALS) { let c = 0; for (; c < max && ne(n, BIGINT_0); ++c) n = and(n, sub(n, BIGINT_1)); return c; } // src/cube.ts var Cube = class _Cube { constructor(bigint, set, hash) { this._bigint = bigint; this._set = set; this._hash = hash; } get bigint() { return this._bigint; } get set() { return this._set; } get hash() { return this._hash; } get empty() { for (const s of this._set) if (this._set.has(s ^ 1)) return true; return false; } raise(n) { if (!this._set.has(n)) return this; const b = xor(this._bigint, BIGINT_INDEX[n]); const s = new Set(this._set); s.delete(n); const h = this._hash ^ 1 << n; return new _Cube(b, s, h); } lower(n) { if (this._set.has(n)) return this; const b = or(this._bigint, BIGINT_INDEX[n]); const s = /* @__PURE__ */ new Set([...this._set, n]); const h = this._hash ^ 1 << n; return new _Cube(b, s, h); } covers(c) { return this.set.size <= c.set.size && eq(and(this.bigint, c.bigint), this.bigint); } static parse(str) { const parts = str.split(""); let bigint = BIGINT_0; let hash = 0; const set = /* @__PURE__ */ new Set(); for (const [idx, p] of parts.entries()) { if (p === "-") continue; const i = idx * 2; if (p === "0") { bigint = or(bigint, BIGINT_INDEX[i]); set.add(i); hash ^= 1 << i; } else if (p === "1") { bigint = or(bigint, BIGINT_INDEX[i + 1]); set.add(i + 1); hash ^= 2 << i; } else if (p === "/") { bigint = or(bigint, BIGINT_INDEX[i]); set.add(i); bigint = or(bigint, BIGINT_INDEX[i + 1]); set.add(i + 1); hash ^= 3 << i; } else { throw new Error("Invalid cube string"); } } return new _Cube(bigint, set, hash); } toString() { let litStr = this.bigint.toString(2); if (litStr.length % 2) litStr = "0" + litStr; const arr = litStr.match(/.{2}/g).reverse(); const str = arr.map((s) => { if (s === "00") return "-"; else if (s === "01") return "0"; else if (s === "10") return "1"; return "/"; }); return str.join(""); } static from(lits) { let bigint = BIGINT_0; let hash = 0; const set = /* @__PURE__ */ new Set(); for (const i of lits) { set.add(i); hash ^= 1 << i; bigint = or(bigint, BIGINT_INDEX[i]); } return new _Cube(bigint, set, hash); } static fromBigInt(bigint) { let hash = 0; const set = /* @__PURE__ */ new Set(); let offset = 0; let n = bigint; while (ne(n, BIGINT_0)) { let b = toNumber(asIntN(32, n)); hash ^= b; n = rshift(n, BIGINT_32); let i = Math.clz32(b); while (i < 32) { const li = 31 - i; set.add(offset + li); b ^= 1 << li; i = Math.clz32(b); } offset += 32; } return new _Cube(bigint, set, hash); } }; // src/cover.ts var Cover = class _Cover { constructor(cubes, count, bigint) { this._cubes = cubes; this._count = count; this._bigint = bigint; } static from(arg) { const cubes = []; let bigint = BIGINT_0; const count = []; for (const cube of arg) { bigint = or(bigint, cube.bigint); cubes.push(cube); for (const i of cube.set) { while (count.length <= i + 1 - i % 2) count.push(0); ++count[i]; } } return new _Cover(cubes, count, bigint); } get bigint() { return this._bigint; } get cubes() { return this._cubes; } count(i) { return this._count[i]; } filter(callback) { const count = this._count.slice(); let bigint = this._bigint; const cubes = this._cubes.filter((c) => { if (callback(c)) return true; for (const i of c.set) if (!--count[i]) bigint = xor(bigint, BIGINT_INDEX[i]); return false; }); return new _Cover(cubes, count, bigint); } pop() { const c = this._cubes.pop(); if (!c) return void 0; for (const i of c.set) { if (!--this._count[i]) this._bigint = xor(this._bigint, BIGINT_INDEX[i]); } return c; } push(c) { const r = this._cubes.push(c); for (const i of c.set) ++this._count[i]; this._bigint = or(this._bigint, c.bigint); return r; } unshift(c) { const r = this._cubes.unshift(c); for (const i of c.set) ++this._count[i]; this._bigint = or(this._bigint, c.bigint); return r; } }; // src/sat.ts function sat(cover, lit = BIGINT_0, free = and(cover.bigint, not(or(lit, invBi(lit))))) { let repeat = false; let contradiction = false; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) contradiction = true; return true; }); if (contradiction) return false; } while (repeat); if (cover.cubes.length <= 1) return true; free = and(cover.bigint, free); let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); const elim = /* @__PURE__ */ new Set(); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } else if (count === cover.cubes.length) { return true; } else { elim.add(f); lit = or(lit, BIGINT_INDEX[count0 ? f : f + 1]); free = xor(free, BIGINT_INDEX[count0 ? f + 1 : f]); } sparseness = Math.max(sparseness, count); } if (binate === -1) return true; if (sparseness * 3 < cover.cubes.length && freeIdx.length - elim.size > 8) { const covers = componentReduction( cover.cubes, freeIdx.filter((f) => !elim.has(f)) ); if (covers.length > 1) { for (const c of covers) if (!sat(Cover.from(c), lit, free)) return false; return true; } } if (sat( cover, or(lit, BIGINT_INDEX[binate]), xor(free, BIGINT_INDEX[binate + 1]) )) return true; return sat( cover, or(lit, BIGINT_INDEX[binate + 1]), xor(free, BIGINT_INDEX[binate]) ); } // src/all-sat.ts function allSat(cover, mask = cover.bigint, lit = BIGINT_0, aux = new Set(bitIndices(and(cover.bigint, not(mask)))), free = cover.bigint) { let repeat = false; let contradiction = false; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) contradiction = true; return true; }); if (contradiction) return []; } while (repeat); if (!cover.cubes.length) return [Cube.fromBigInt(and(mask, lit))]; free = and(cover.bigint, free); const res = []; if (cover.cubes.length === 1) { const l = and(mask, lit); for (const f of bitIndices(and(mask, free))) res.push(Cube.fromBigInt(or(l, BIGINT_INDEX[f]))); return res; } let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); const elim = /* @__PURE__ */ new Set(); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } else if (aux.has(f)) { elim.add(f); free = xor(free, BIGINT_INDEX[count0 ? f : f + 1]); } else if (count === cover.cubes.length) { elim.add(f); res.push( Cube.fromBigInt( and(mask, or(lit, BIGINT_INDEX[count0 ? f : f + 1])) ) ); free = xor(free, BIGINT_INDEX[count0 ? f : f + 1]); } sparseness = Math.max(sparseness, count); } if (sparseness * 3 < cover.cubes.length && freeIdx.length - elim.size > 8) { const covers = componentReduction( cover.cubes, freeIdx.filter((f) => !elim.has(f)) ); if (covers.length > 1) { const cov = Cover.from(covers.pop()); let res12 = allSat(cov, mask, lit, aux, free); for (const c of covers) { if (!res12.length) return res; const res22 = allSat(Cover.from(c), mask, lit, aux, free); const res3 = []; for (const r1 of res12) { for (const r2 of res22) res3.push(Cube.fromBigInt(or(r1.bigint, r2.bigint))); } res12 = res3; } return [...res, ...res12]; } } if (binate === -1) { allSatUnate(cover, res, mask, lit, free); return res; } const res1 = allSat( cover, mask, or(lit, BIGINT_INDEX[binate]), aux, xor(free, BIGINT_INDEX[binate + 1]) ); let res2 = allSat( cover, mask, or(lit, BIGINT_INDEX[binate + 1]), aux, xor(free, BIGINT_INDEX[binate]) ); if (aux.has(binate)) { const remove = /* @__PURE__ */ new Set(); outer: for (const r1 of res1) { for (const r2 of res2) { if (r1.hash === r2.hash && eq(r1.bigint, r2.bigint)) { remove.add(r2); continue outer; } } } if (remove.size) res2 = res2.filter((r) => !remove.has(r)); } else { const hashMask = 3 << binate; const hashMaskBi = or(BIGINT_INDEX[binate], BIGINT_INDEX[binate + 1]); const remove = /* @__PURE__ */ new Set(); outer: for (const [i, r1] of res1.entries()) { const h = r1.hash ^ hashMask; for (const r2 of res2) { if (r2.hash === h && eq(xor(r1.bigint, r2.bigint), hashMaskBi)) { res1[i] = Cube.fromBigInt(and(r1.bigint, r2.bigint)); remove.add(r2); continue outer; } } } if (remove.size) res2 = res2.filter((r) => !remove.has(r)); } return [...res, ...res1, ...res2]; } function allSatUnate(cover, res, mask, lit, free) { let repeat = false; let tautology3 = false; let largestCubeSize = MAX_LITERALS; let largestCube = BIGINT_0; do { largestCubeSize = MAX_LITERALS; largestCube = BIGINT_0; repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, largestCubeSize); if (pc === 1) { repeat = true; lit = or(lit, diff); return false; } if (pc < largestCubeSize) { largestCube = diff; largestCubeSize = pc; } if (pc === 0) tautology3 = true; return true; }); if (tautology3) return; } while (repeat); if (!cover.cubes.length) { res.push(Cube.fromBigInt(and(mask, lit))); return; } const freeIdx = bitIndices(largestCube); if (cover.cubes.length === 1) { const l = and(mask, lit); for (const f of freeIdx) res.push(Cube.fromBigInt(or(l, BIGINT_INDEX[f]))); return; } let unate = -1; let unateness = 0; let sparseness = 0; for (const f of freeIdx) { const count = cover.count(f); sparseness = Math.max(sparseness, count); if (count > unateness) { unate = f; unateness = count; } } allSatUnate(cover, res, mask, or(lit, BIGINT_INDEX[unate]), free); allSatUnate(cover, res, mask, lit, xor(free, BIGINT_INDEX[unate])); } // src/tautology.ts function tautology(cover, lit = BIGINT_0, free = and(cover.bigint, not(or(lit, invBi(lit))))) { let repeat = false; let taut = false; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) taut = true; return true; }); if (taut) return true; } while (repeat); if (cover.cubes.length <= 1) return false; free = and(cover.bigint, free); let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); const elim = /* @__PURE__ */ new Set(); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } else if (count === cover.cubes.length) { return false; } else { elim.add(f); lit = or(lit, BIGINT_INDEX[count0 ? f : f + 1]); free = xor(free, BIGINT_INDEX[count0 ? f + 1 : f]); } sparseness = Math.max(sparseness, count); } if (binate === -1) return false; if (sparseness * 3 < cover.cubes.length && freeIdx.length - elim.size > 8) { const covers = componentReduction( cover.cubes, freeIdx.filter((f) => !elim.has(f)) ); if (covers.length > 1) { for (const cov of covers) if (tautology(Cover.from(cov), lit, free)) return true; return false; } } if (!tautology( cover, or(lit, BIGINT_INDEX[binate]), xor(free, BIGINT_INDEX[binate + 1]) )) return false; return tautology( cover, or(lit, BIGINT_INDEX[binate + 1]), xor(free, BIGINT_INDEX[binate]) ); } // src/complement.ts function complement(cover, lit = BIGINT_0, free = cover.bigint) { let repeat = false; let taut = false; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) taut = true; return true; }); if (taut) return []; } while (repeat); if (!cover.cubes.length) return [Cube.fromBigInt(invBi(lit))]; free = and(cover.bigint, free); const res = []; if (cover.cubes.length === 1) { const inv = invBi(lit); for (const f of bitIndices(free)) res.push(Cube.fromBigInt(or(inv, BIGINT_INDEX[f ^ 1]))); return res; } let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); const elim = /* @__PURE__ */ new Set(); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } else if (count === cover.cubes.length) { elim.add(f); res.push( Cube.fromBigInt(invBi(or(lit, BIGINT_INDEX[count0 ? f : f + 1]))) ); free = xor(free, BIGINT_INDEX[count0 ? f : f + 1]); } sparseness = Math.max(sparseness, count); } if (sparseness * 3 < cover.cubes.length && freeIdx.length - elim.size > 8) { const covers = componentReduction( cover.cubes, freeIdx.filter((f) => !elim.has(f)) ); if (covers.length > 1) { const cov = Cover.from(covers.pop()); let res12 = complement(cov, lit, free); for (const c of covers) { if (!res12.length) return res; const res22 = complement(Cover.from(c), lit, free); const res3 = []; for (const r1 of res12) { for (const r2 of res22) res3.push(Cube.fromBigInt(or(r1.bigint, r2.bigint))); } res12 = res3; } return [...res, ...res12]; } } if (binate === -1) { complementUnate(cover, res, lit, free); return res; } const res1 = complement( cover, or(lit, BIGINT_INDEX[binate]), xor(free, BIGINT_INDEX[binate + 1]) ); let res2 = complement( cover, or(lit, BIGINT_INDEX[binate + 1]), xor(free, BIGINT_INDEX[binate]) ); const hashMask = 3 << binate; const hashMaskBi = or(BIGINT_INDEX[binate], BIGINT_INDEX[binate + 1]); const remove = /* @__PURE__ */ new Set(); outer: for (const [i, r1] of res1.entries()) { const h = r1.hash ^ hashMask; for (const r2 of res2) { if (r2.hash === h && eq(xor(r1.bigint, r2.bigint), hashMaskBi)) { res1[i] = Cube.fromBigInt(and(r1.bigint, r2.bigint)); remove.add(r2); continue outer; } } } if (remove.size) res2 = res2.filter((r) => !remove.has(r)); return [...res, ...res1, ...res2]; } function complementUnate(cover, res, lit, free) { let repeat = false; let tautology3 = false; let largestCubeSize = MAX_LITERALS; let largestCube = BIGINT_0; do { largestCubeSize = MAX_LITERALS; largestCube = BIGINT_0; repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, largestCubeSize); if (pc === 1) { repeat = true; lit = or(lit, diff); return false; } if (pc < largestCubeSize) { largestCube = diff; largestCubeSize = pc; } if (pc === 0) tautology3 = true; return true; }); if (tautology3) return; } while (repeat); if (!cover.cubes.length) { res.push(Cube.fromBigInt(invBi(lit))); return; } const freeIdx = bitIndices(largestCube); if (cover.cubes.length === 1) { const inv = invBi(lit); for (const f of freeIdx) res.push(Cube.fromBigInt(or(inv, BIGINT_INDEX[f ^ 1]))); return; } let unate = -1; let unateness = 0; let sparseness = 0; for (const f of freeIdx) { const count = cover.count(f); sparseness = Math.max(sparseness, count); if (count > unateness) { unate = f; unateness = count; } } complementUnate(cover, res, or(lit, BIGINT_INDEX[unate]), free); complementUnate(cover, res, lit, xor(free, BIGINT_INDEX[unate])); } // src/espresso.ts function COVERS(cover, cube) { return tautology(cover, invBi(cube.bigint)); } function EXPAND1(cube, onSet, offSet, canRaise, bias) { const cubeInv = invBi(cube.bigint); let blockingMatrix = offSet.map( (c) => new Set(bitIndices(and(cubeInv, c.bigint))) ); blockingMatrix = blockingMatrix.filter((c) => c.size); let coveringMatrix = onSet.map( (c) => new Set(bitIndices(and(cube.bigint, xor(cube.bigint, c.bigint)))) ); coveringMatrix = coveringMatrix.filter((c) => c.size); const toRaise = new Set([...cube.set].sort(bias)); const count = []; for (let i = Math.max(...toRaise); i >= 0; --i) count.push(0); for (const c of coveringMatrix) for (const i of c) ++count[i]; while (coveringMatrix.length && blockingMatrix.length) { const essential = /* @__PURE__ */ new Set(); for (const b of blockingMatrix) if (b.size === 1) essential.add(b.values().next().value); if (essential.size) { for (const e of essential) { toRaise.delete(e ^ 1); blockingMatrix = blockingMatrix.filter((b) => !b.has(e)); coveringMatrix = coveringMatrix.filter((c) => !c.has(e ^ 1)); } const inessentialColumns = new Set(toRaise); for (const b of blockingMatrix) for (const i of b) inessentialColumns.delete(i ^ 1); for (const i of inessentialColumns) { toRaise.delete(i); cube = cube.raise(i); coveringMatrix = coveringMatrix.filter( (c) => !(c.delete(i) && !c.size) ); } if (!blockingMatrix.length || !coveringMatrix.length) break; } const feasible = /* @__PURE__ */ new Set(); for (const c of coveringMatrix) if (c.size === 1) feasible.add(c.values().next().value); if (feasible.size) { let cnt2 = 0; let raise2 = -1; for (const r of [...feasible].sort(bias)) { if (count[r] > cnt2 && canRaise(r, cube.set)) { cnt2 = count[r]; raise2 = r; } } if (raise2 >= 0) { toRaise.delete(raise2); cube = cube.raise(raise2); coveringMatrix = coveringMatrix.filter( (c) => !(c.delete(raise2) && !c.size) ); for (const b of blockingMatrix) b.delete(raise2 ^ 1); continue; } } let cnt = 0; let raise = -1; for (const r of toRaise) { if (count[r] > cnt && canRaise(r, cube.set)) { cnt = count[r]; raise = r; } } if (raise < 0) { toRaise.clear(); break; } toRaise.delete(raise); cube = cube.raise(raise); for (const c of coveringMatrix) c.delete(raise); for (const b of blockingMatrix) b.delete(raise ^ 1); } if (blockingMatrix.length) { const MINLOW = MINUCOV(blockingMatrix); for (const m of MINLOW) toRaise.delete(m ^ 1); } for (const t of toRaise) if (canRaise(t, cube.set)) cube = cube.raise(t); return cube; } function EXPAND1_PRESTO(cube, onSet, cover, canRaise, bias) { let coveringMatrix = onSet.map( (c) => new Set(bitIndices(and(cube.bigint, xor(cube.bigint, c.bigint)))) ); coveringMatrix = coveringMatrix.filter((c) => c.size); const toRaise = [...cube.set]; const count = []; for (let i = Math.max(...toRaise); i >= 0; --i) count.push(0); for (const c of coveringMatrix) for (const i of c) ++count[i]; while (toRaise.length) { const feasible = /* @__PURE__ */ new Set(); for (const c of coveringMatrix) if (c.size === 1) feasible.add(c.values().next().value); toRaise.sort( (a, b) => +feasible.has(a) - +feasible.has(b) || count[a] - count[b] || bias(b, a) ); const cantRaise = []; while (toRaise.length) { const r = toRaise.pop(); if (!canRaise(r, cube.set)) { cantRaise.unshift(r); continue; } const nc = cube.raise(r); if (!COVERS(cover, nc)) { coveringMatrix = coveringMatrix.filter((c) => !c.has(r)); toRaise.push(...cantRaise); break; } cube = nc; for (const c of coveringMatrix) c.delete(r); coveringMatrix = coveringMatrix.filter((c) => c.size); toRaise.push(...cantRaise.splice(0)); } } return cube; } function EXPAND(onSet, dcSet, offSet, primes, canRaise, bias) { if (!onSet.length) return onSet; onSet = onSet.slice(); onSet.sort((a, b) => a.set.size - b.set.size); const firstCube = onSet[0]; do { let cube = onSet[0]; if (!primes.has(cube)) { cube = offSet ? EXPAND1(cube, onSet, offSet, canRaise, bias) : EXPAND1_PRESTO( cube, onSet, Cover.from([...onSet, ...dcSet]), canRaise, bias ); } onSet = onSet.filter((o) => !cube.covers(o)); onSet.push(cube); } while (onSet[0].set.size >= firstCube.set.size && ne(onSet[0].bigint, firstCube.bigint)); return onSet; } function REDUNDANT(onSet, dcSet) { const cover = Cover.from([...dcSet, ...onSet]); const E = []; const R = []; for (let i = 0; i < onSet.length; ++i) { const cube = cover.pop(); if (COVERS(cover, cube)) R.push(cube); else E.push(cube); cover.unshift(cube); } return [E, R]; } function PARTIALLY_REDUNDANT(R, E, dcSet) { const cover = Cover.from([...dcSet, ...E]); return R.filter((c) => !COVERS(cover, c)); } function MAXCLIQ(covers) { const neighbors = /* @__PURE__ */ new Map(); const len = covers.length; for (let i = 0; i < len; ++i) neighbors.set(i, /* @__PURE__ */ new Set()); for (let i = 0; i < len; ++i) { const c1 = covers[i]; const set1 = neighbors.get(i); for (let j = i + 1; j < len; ++j) { const c2 = covers[j]; let connected = false; for (const n of c1) { if (c2.has(n)) { connected = true; break; } } if (!connected) { const set2 = neighbors.get(j); set1.add(j); set2.add(i); } } } const keys = Array.from(covers.keys()); keys.sort( (a, b) => neighbors.get(b).size - neighbors.get(a).size ); let solution = []; let solutionWeight = 0; let exitCounter = 0; function bronKerbosch(R, P, X) { if (exitCounter > len) return; if (!P.length && !X.length) { ++exitCounter; if (R.length >= solution.length) { const w = R.reduce( (acc, cur) => acc + neighbors.get(cur).size, 0 ); if (w > solutionWeight || R.length > solution.length) { solution = R; solutionWeight = w; exitCounter = 0; } } return; } const excl = /* @__PURE__ */ new Set(); const pivotNeigh = neighbors.get(P[0]); for (const p of P) { if (pivotNeigh.has(p)) continue; const neigh = neighbors.get(p); const newP = P.filter((i) => neigh.has(i) && !excl.has(i)); const newX = X.filter((i) => neigh.has(i)); bronKerbosch([...R, p], newP, newX); if (solution.length > R.length + P.length) return; X.push(p); excl.add(p); } } bronKerbosch([], keys, []); return solution.map((i) => covers[i]); } function WEED(covers, cover) { let points = covers.map( (m) => new Set([...m].filter((x) => cover.has(x))) ); const essential = /* @__PURE__ */ new Set(); for (; ; ) { for (const point of points) { if (point.size === 1) { const [p] = point; essential.add(p); } } points = points.filter((point) => { for (const p of point) if (essential.has(p)) return false; return point.size > 1; }); if (!points.length) break; const candidates = /* @__PURE__ */ new Map(); for (const point of points) { for (const p of point) if (!candidates.has(p)) candidates.set(p, /* @__PURE__ */ new Set()); if (point.size === 2) { const [p1, p2] = point; const cand1 = candidates.get(p1); const cand2 = candidates.get(p2); cand1.add(p2); cand2.add(p1); } } const eliminate = Array.from(candidates).reduce( (acc, cur) => cur[1].size < acc[1].size ? cur : acc )[0]; for (const point of points) point.delete(eliminate); } return essential; } function MINUCOV(covers) { let res = /* @__PURE__ */ new Set(); const I = MAXCLIQ(covers); let remaining = covers; for (const i of I) { const freq = /* @__PURE__ */ new Map(); for (const c of covers) { for (const j of c) { if (i.has(j)) { let s = freq.get(j); if (!s) freq.set(j, s = /* @__PURE__ */ new Set()); s.add(c); } } } const [v, set] = Array.from(freq).reduce( (acc, cur) => cur[1].size > acc[1].size ? cur : acc ); remaining = remaining.filter((m) => !set.has(m)); res.add(v); } res = WEED(covers, res); if (remaining.length) res = /* @__PURE__ */ new Set([...res, ...MINUCOV(remaining)]); return res; } function LTAUT1(cover, index, lit, free) { let repeat = false; let dc = false; const S = []; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1 && !index.has(c)) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) { const i = index.get(c); if (i != null) { S.push(i); return false; } dc = true; } return true; }); if (dc) return []; } while (repeat); if (cover.cubes.length <= 1) return [new Set(S)]; free = and(cover.bigint, free); let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); const elim = /* @__PURE__ */ new Set(); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } else { elim.add(f); lit = or(lit, BIGINT_INDEX[count0 ? f : f + 1]); free = xor(free, BIGINT_INDEX[count0 ? f + 1 : f]); } sparseness = Math.max(sparseness, count); } if (binate === -1) return [new Set(S)]; if (sparseness * 3 < cover.cubes.length && freeIdx.length - elim.size > 8) { const covers = componentReduction( cover.cubes, freeIdx.filter((f) => !elim.has(f)) ); if (covers.length > 1) { let res = [new Set(S)]; for (const cov of covers) { const res22 = res; res = LTAUT1(Cover.from(cov), index, lit, free); const res3 = []; for (const r1 of res) for (const r2 of res22) res3.push(/* @__PURE__ */ new Set([...r1, ...r2])); res = res3; } return res; } } let res1 = LTAUT1( cover, index, or(lit, BIGINT_INDEX[binate]), xor(free, BIGINT_INDEX[binate + 1]) ); let res2 = LTAUT1( cover, index, or(lit, BIGINT_INDEX[binate + 1]), xor(free, BIGINT_INDEX[binate]) ); const rem1 = /* @__PURE__ */ new Set(); const rem2 = /* @__PURE__ */ new Set(); for (const r1 of res1) { for (const r2 of res2) { if (r1.size <= r2.size) { let found = true; for (const n of r1) { if (!r2.has(n)) { found = false; break; } } if (found) rem2.delete(r2); } else { let found = true; for (const n of r2) { if (!r1.has(n)) { found = false; break; } } if (found) rem1.delete(r1); } } } if (rem1.size) res1 = res1.filter((r) => !rem1.has(r)); if (rem2.size) res2 = res2.filter((r) => !rem2.has(r)); return [...res1, ...res2].map((s) => /* @__PURE__ */ new Set([...S, ...s])); } function NOCOVERMAT(Rp, E, dcSet) { const res = /* @__PURE__ */ new Map(); const index = /* @__PURE__ */ new WeakMap(); for (const [i, p] of Rp.entries()) index.set(p, i); const cover = Cover.from([...Rp, ...E, ...dcSet]); for (const r of Rp) { const lit = invBi(r.bigint); const free = and(cover.bigint, not(or(r.bigint, lit))); const res2 = LTAUT1(cover, index, lit, free); for (const r2 of res2) { let hash = 0; for (const i of r2) hash ^= 1 << i; const grp = res.get(hash); if (!grp) { res.set(hash, [r2]); continue; } let found = false; loop: for (const g of grp) { if (g.size !== r2.size) continue; for (const i of g) if (!r2.has(i)) continue loop; found = true; break; } if (!found) grp.push(r2); } } return [...res.values()].flat(); } function MINIMAL_IRREDUNDANT(Rp, E, dcSet) { const CM = NOCOVERMAT(Rp, E, dcSet); const J = MINUCOV(CM); return Rp.filter((c, i) => J.has(i)); } function IRREDUNDANT_COVER(onSet, dcSet) { const [E, R] = REDUNDANT(onSet, dcSet); const Rp = PARTIALLY_REDUNDANT(R, E, dcSet); const Rc = MINIMAL_IRREDUNDANT(Rp, E, dcSet); return [...E, ...Rc]; } function ESSENTIAL_PRIMES(onSet, dcSet) { const cover = [...dcSet, ...onSet]; const res = []; for (let i = 0; i < onSet.length; ++i) { const ess = cover.pop(); const essInv = invBi(ess.bigint); const cov = []; for (const cube of cover) { const conflict = and(essInv, cube.bigint); const dist = popcount(conflict, 2); if (dist === 0) cov.push(cube); else if (dist === 1) cov.push(Cube.fromBigInt(xor(cube.bigint, conflict))); } if (!tautology(Cover.from(cov), essInv)) res.push(ess); cover.unshift(ess); } return res; } function CUBE_ORDER(cubes) { if (cubes.length <= 1) return; const seed = cubes.reduce( (acc, cur) => cur.set.size <= acc.set.size ? cur : acc ); cubes.sort((a, b) => { const pc1 = popcount(and(a.bigint, seed.bigint)); const dist1 = a.set.size - pc1 + (seed.set.size - pc1); const pc2 = popcount(and(b.bigint, seed.bigint)); const dist2 = b.set.size - pc2 + (seed.set.size - pc2); return dist1 - dist2; }); } function SCCC(cover, lit, free) { let repeat = false; let taut = false; do { repeat = false; cover = cover.filter((c) => { if (ne(and(lit, c.bigint), BIGINT_0)) return false; const diff = and(c.bigint, free); const pc = popcount(diff, 2); if (pc === 1) { repeat = true; lit = or(lit, diff); if (eq(and(BIGINT_ODD, diff), BIGINT_0)) free = xor(free, rshift(diff, BIGINT_1)); else free = xor(free, lshift(diff, BIGINT_1)); return false; } if (pc === 0) taut = true; return true; }); if (taut) return BIGINT_0C; } while (repeat); if (cover.cubes.length <= 1) return lit; free = and(cover.bigint, free); let binate = -1; let binateness = 0; let binatenessMin = 0; let sparseness = 0; const freeIdx = bitIndicesOdd(free); for (const f of freeIdx) { const count0 = cover.count(f); const count1 = cover.count(f + 1); const count = count0 + count1; if (count0 && count1) { if (count >= binateness) { const min = Math.min(count0, count1); if (count > binateness || min > binatenessMin) { binate = f; binateness = count; binatenessMin = min; } } } sparseness = Math.max(sparseness, count); } if (binate === -1) return lit; if (sparseness * 3 < cover.cubes.length && freeIdx.length > 8) { const covers = componentReduction(cover.cubes, freeIdx); if (covers.length > 1) { for (const cov of covers) lit = or(lit, SCCC(Cover.from(cov), lit, free)); return lit; } } const res1 = SCCC( cover, or(lit, BIGINT_INDEX[binate]), xor(free, BIGINT_INDEX[binate + 1]) ); const res2 = SCCC( cover, or(lit, BIGINT_INDEX[binate + 1]), xor(free, BIGINT_INDEX[binate]) ); return and(res1, res2); } function REDUCE(onSet, dcSet, primes, canLower) { onSet = onSet.slice(); CUBE_ORDER(onSet); for (let i = onSet.length; i > 0; --i) { const cube = onSet.shift(); const cubeInv = invBi(cube.bigint); const cubeMask = or(cube.bigint, cubeInv); const cov = [...onSet, ...dcSet].filter( (c) => eq(and(cubeInv, c.bigint), BIGINT_0) ); const sccc = SCCC(Cover.from(cov), cubeInv, not(cubeMask)); if (gte(sccc, BIGINT_0)) { if (eq(sccc, cubeInv)) { onSet.push(cube); primes.add(cube); } else { let reduced = cube; for (let toLower = bitIndices(sccc); toLower.length; ) { const toLowerRetry = []; for (const t of toLower) { if (canLower(t ^ 1, reduced.set)) reduced = reduced.lower(t ^ 1); else toLowerRetry.push(t); } if (toLowerRetry.length === toLower.length) break; toLower = toLowerRetry; } onSet.push(reduced); } } } return onSet; } function MAXIMUM_REDUCTION(onSet, dcSet, canLower) { const reducedCubes = []; for (let i = onSet.length; i > 0; --i) { const cube = onSet.shift(); const cubeInv = invBi(cube.bigint); const cubeMask = or(cube.bigint, cubeInv); const cov = [...onSet, ...dcSet].filter( (c) => eq(and(cubeInv, c.bigint), BIGINT_0) ); const sccc = SCCC(Cover.from(cov), cubeInv, not(cubeMask)); if (ne(sccc, cubeInv) && gte(sccc, BIGINT_0)) { let reduced = cube; for (let toLower = bitIndices(sccc); toLower.length; ) { const toLowerRetry = []; for (const t of toLower) { if (canLower(t ^ 1, reduced.set)) reduced = reduced.lower(t ^ 1); else toLowerRetry.push(t); } if (toLowerRetry.length === toLower.length) break; toLower = toLowerRetry; } reducedCubes.push(reduced); } onSet.push(cube); } return reducedCubes; } function LAST_GASP(onSet, dcSet, offSet, canRaise, canLower, bias) { const reduced = MAXIMUM_REDUCTION(onSet, dcSet, canLower); const newCubes = []; const cover = offSet ? null : Cover.from([...onSet, ...dcSet]); for (let i = reduced.length; i > 0; --i) { const cube = reduced.shift(); const expanded = offSet ? EXPAND1(cube, reduced, offSet, canRaise, bias) : EXPAND1_PRESTO(cube, reduced, cover, canRaise, bias); for (const c of reduced) if (expanded.covers(c)) newCubes.push(expanded); reduced.push(cube); } if (!newCubes.length) return onSet; return IRREDUNDANT_COVER([...onSet, ...newCubes], dcSet); } function COST(cover) { return cover.reduce((a, c) => a + c.set.size, 0); } function espresso(onSet, dcSet, offSet, canRaise = () => true, canLower = () => true, bias = () => 0) { if (!onSet.length) return onSet; const primes = /* @__PURE__ */ new WeakSet(); onSet = EXPAND(onSet, dcSet, offSet, primes, canRaise, bias); onSet = IRREDUNDANT_COVER(onSet, dcSet); const essentialPrimes = ESSENTIAL_PRIMES(onSet, dcSet); if (essentialPrimes.length) { onSet = onSet.filter((c) => !essentialPrimes.includes(c)); dcSet = [...dcSet, ...essentialPrimes]; } let cost = onSet.length * MAX_LITERALS; for (; ; ) { let onSet2 = REDUCE(onSet, dcSet, primes, canLower); onSet2 = EXPAND(onSet2, dcSet, offSet, primes, canRaise, bias); onSet2 = IRREDUNDANT_COVER(onSet2, dcSet); let cost2 = COST(onSet2); if (cost2 <= cost) onSet = onSet2; if (cost2 >= cost) { onSet2 = LAST_GASP(onSet, dcSet, offSet, canRaise, canLower, bias); cost2 = COST(onSet2); if (cost2 <= cost) onSet = onSet2; if (cost2 >= cost) break; } cost = cost2; } return [...essentialPrimes, ...onSet]; } // src/main.ts function toCubes(input) { return input.map((i) => Cube.from(i)); } function fromCubes(input) { return input.map((i) => [...i.set]); } function sat2(cnf) { const cover = Cover.from(toCubes(cnf)); return sat(cover); } function allSat2(cnf) { const cover = Cover.from(toCubes(cnf)); const res = allSat(cover); return fromCubes(res); } function tautology2(dnf) { const cover = Cover.from(toCubes(dnf)); return tautology(cover); } function complement2(dnf) { const cover = Cover.from(toCubes(dnf)); const res = complement(cover); return fromCubes(res); } function espresso2(onSet, dcSet = [], options = {}) { const _onSet = toCubes(onSet).filter((c) => !c.empty); const _dcSet = toCubes(dcSet).filter((c) => !c.empty); const _offSet = options.computeOffSet ? complement(Cover.from([..._onSet, ..._dcSet])) : void 0; const res = espresso( _onSet, _dcSet, _offSet, options.canRaise, options.canLower, options.bias ); return fromCubes(res); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { allSat, complement, espresso, sat, tautology });