<p class="hint">This <strong>width-5 permutation branching program</strong> evaluates
<strong>(x₁ ∧ x₂) ∨ x₃</strong> using only permutations of {0,1,2,3,4}.
Toggle the input bits, then click <strong>Run program</strong> to watch the composition
build step by step. The program <em>accepts</em> when the final permutation is the identity.</p>
<div class="inputs">
<label><input type="checkbox" id="x1"> x₁</label>
<label><input type="checkbox" id="x2"> x₂</label>
<label><input type="checkbox" id="x3"> x₃</label>
</div>
<div id="steps-area"></div>
<div class="status" id="status">Set bits and run the program.</div>
<div class="btns">
<button id="run" type="button">Run program</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.inputs { display: flex; gap: 1.4rem; margin-bottom: .9rem; }
.inputs label { display: flex; align-items: center; gap: .4rem; font-weight: 700;
font-size: 1rem; cursor: pointer; user-select: none; }
input[type=checkbox] { width: 18px; height: 18px; cursor: pointer; accent-color: #1d3557; }
#steps-area { display: flex; flex-direction: column; gap: 5px; margin-bottom: .7rem;
max-height: 260px; overflow-y: auto; }
.step { display: grid; grid-template-columns: 100px 60px 20px 1fr;
align-items: center; gap: .4rem; padding: 5px 8px;
border-radius: 7px; background: #edf2f7; border: 1px solid #cdd9e3;
font-size: .83rem; font-family: ui-monospace, monospace; }
.step.accepted { background: #d4edda; border-color: #6abf8a; }
.step.rejected { background: #fde8e8; border-color: #e08b8b; }
.step-label { font-weight: 700; color: #1d3557; }
.step-bit { color: #555; font-size: .8rem; }
.arrow { color: #aaa; text-align: center; }
.perm { display: flex; gap: 3px; }
.perm span { display: inline-flex; align-items: center; justify-content: center;
width: 20px; height: 20px; border-radius: 4px;
background: #dce8f5; font-size: .78rem; font-weight: 600; color: #1d3557; }
.step.accepted .perm span { background: #b8e0c8; color: #0a5c22; }
.step.rejected .perm span { background: #f5c6c6; color: #9b1c1c; }
.status { font-size: .95rem; font-weight: 600; margin: .3rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.idle { color: #555; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Barrington's theorem: NC¹ = width-5 permutation branching programs.
// We implement a VERIFIED 12-step width-5 branching program for (x1 AND x2) OR x3.
//
// Construction via the NOR-commutator technique:
// OR(AND(x1,x2), x3) uses programs P_{NOT-AND} and P_{NOT-x3}:
// [P_{NOT-AND}, P_{NOT-x3}](x) = P_{NOT-AND}(x) * P_{NOT-x3}(x)
// * inv(P_{NOT-AND}(x)) * inv(P_{NOT-x3}(x))
// This equals the identity permutation iff NOT(NOT-AND and NOT-x3) = AND(x1,x2) OR x3.
//
// Permutations of {0,1,2,3,4} (width-5 means 5 states):
// e = [0,1,2,3,4] identity
// a = [1,2,3,4,0] 5-cycle (0 1 2 3 4)
// ai = [4,0,1,2,3] a⁻¹
// b = [1,2,0,3,4] 3-cycle (0 1 2)
// bi = [2,0,1,3,4] b⁻¹
// COMM = a*b*ai*bi = [0,4,1,3,2] commutator [a,b]
// COMMi= COMM⁻¹ = [0,2,4,3,1]
// d = [1,2,3,4,0] = a (used for P_{NOT-x3})
// di = [4,0,1,2,3] = ai
//
// The 12-step program (all perms in S₅, most from A₅):
// Steps 1-5: P_{NOT-AND}(x1,x2) → outputs COMMi iff NAND(x1,x2), e iff AND
// Step 6: P_{NOT-x3}(x3) → outputs a iff x3=0 (NOT x3=1), e iff x3=1
// Steps 7-11: inv(P_{NOT-AND})(x1,x2) → outputs COMM iff NAND, e iff AND
// Step 12: inv(P_{NOT-x3})(x3) → outputs ai iff x3=0, e iff x3=1
//
// This is verified correct for all 8 input combinations (see console).
function cp(p, q) { return p.map(i => q[i]); }
function isId(p) { return p.every((v, i) => v === i); }
const e = [0,1,2,3,4];
const a = [1,2,3,4,0];
const ai = [4,0,1,2,3];
const b = [1,2,0,3,4];
const bi = [2,0,1,3,4];
const COMM = cp(cp(cp(a,b),ai),bi); // = [0,4,1,3,2]
const COMMi= [0,2,4,3,1]; // inv(COMM)
const d = a; // generator for P_{NOT-x3}
const di = ai;
// Each step: [varIdx (0=x1,1=x2,2=x3), perm_if_0, perm_if_1]
// 'null' perm means: use e (identity, regardless of variable).
// 'DUMMY' marker for unconditional steps (same perm for 0 and 1).
const PROG = [
// --- P_{NOT-AND}: 5 steps ---
// 4-step NAND commutator [a,b]: output=e iff NAND(x1,x2)
[0, e, a ], // step 1: x1 → {0:e, 1:a}
[1, e, b ], // step 2: x2 → {0:e, 1:b}
[0, e, ai ], // step 3: x1 → {0:e, 1:ai}
[1, e, bi ], // step 4: x2 → {0:e, 1:bi}
[0, COMMi, COMMi], // step 5: unconditional COMMi → output becomes COMMi iff NAND, e iff AND
// --- P_{NOT-x3}: 1 step ---
[2, d, e ], // step 6: x3 → {0:d=a, 1:e}
// --- inv(P_{NOT-AND}): 5 steps (reverse of P_{NOT-AND} with each perm inverted) ---
[0, COMM, COMM ], // step 7: unconditional COMM (inverse of step 5's COMMi)
[1, e, b ], // step 8: x2 → {0:e, 1:b} (inverse of bi = b, reversed from step 4)
[0, e, a ], // step 9: x1 → {0:e, 1:a} (inverse of ai = a, reversed from step 3)
[1, e, bi ], // step 10: x2 → {0:e, 1:bi} (inverse of b = bi, reversed from step 2)
[0, e, ai ], // step 11: x1 → {0:e, 1:ai} (inverse of a = ai, reversed from step 1)
// --- inv(P_{NOT-x3}): 1 step ---
[2, di, e ], // step 12: x3 → {0:di=ai, 1:e}
];
const VAR_NAMES = ['x₁', 'x₂', 'x₃'];
const SECTION_LABELS = [
'P_NOT-AND step 1', 'P_NOT-AND step 2', 'P_NOT-AND step 3', 'P_NOT-AND step 4',
'P_NOT-AND step 5 (∗)',
'P_NOT-x₃',
'P_NOT-AND⁻¹ step 1 (∗)', 'P_NOT-AND⁻¹ step 2', 'P_NOT-AND⁻¹ step 3',
'P_NOT-AND⁻¹ step 4', 'P_NOT-AND⁻¹ step 5',
'P_NOT-x₃⁻¹',
];
// Verify all 8 truth-table cases at load time
function evalProg(x1v, x2v, x3v) {
const inputs = [x1v, x2v, x3v];
let perm = e.slice();
const trace = [];
for (let i = 0; i < PROG.length; i++) {
const [vi, p0, p1] = PROG[i];
const chosen = inputs[vi] ? p1 : p0;
perm = cp(perm, chosen);
trace.push({ step: i + 1, label: SECTION_LABELS[i], varName: VAR_NAMES[vi],
bitVal: inputs[vi], chosen, after: perm.slice() });
}
return { perm, trace };
}
let allOk = true;
for (let v = 0; v < 8; v++) {
const x1v=(v>>2)&1, x2v=(v>>1)&1, x3v=v&1;
const expected = ((x1v && x2v) || x3v) ? 1 : 0;
const { perm } = evalProg(x1v, x2v, x3v);
const got = isId(perm) ? 1 : 0;
if (expected !== got) {
allOk = false;
console.error(fmt('Verification FAILED: x1=%x1v% x2=%x2v% x3=%x3v% expected=%expected% got=%got%', { x1v: x1v, x2v: x2v, x3v: x3v, expected: expected, got: got }));
}
}
if (allOk) console.log('Barrington program verified correct for all 8 inputs.');
// ── UI ────────────────────────────────────────────────────────────────────────────
function permEls(p) {
return '<div class="perm">' + p.map(v => '<span>'+v+'</span>').join('') + '</div>';
}
function renderEmpty() {
const area = document.getElementById('steps-area');
area.innerHTML = fmt('<div class="step"><span class="step-label">Start</span><span></span><span class="arrow">→</span>%permEls%</div>', { permEls: permEls(e) });
}
function renderTrace(trace) {
const area = document.getElementById('steps-area');
const rows = [fmt('<div class="step"><span class="step-label">Start</span><span></span><span class="arrow">→</span>%permEls%</div>', { permEls: permEls(e) })];
trace.forEach(s => {
const accepted = isId(s.after);
const cls = 'step ' + (accepted ? 'accepted' : 'rejected');
const bit = s.bitVal ? '= 1' : '= 0';
rows.push(
'<div class="'+cls+'"><span class="step-label">'+s.label+'</span>'
+ '<span class="step-bit">'+s.varName+bit+'</span>'
+ '<span class="arrow">→</span>'
+ permEls(s.after)
+ '</div>'
);
});
area.innerHTML = rows.join('');
area.scrollTop = area.scrollHeight;
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t; s.className = 'status ' + cls;
}
document.getElementById('run').addEventListener('click', () => {
const x1v = document.getElementById('x1').checked ? 1 : 0;
const x2v = document.getElementById('x2').checked ? 1 : 0;
const x3v = document.getElementById('x3').checked ? 1 : 0;
const { perm, trace } = evalProg(x1v, x2v, x3v);
renderTrace(trace);
const accepted = isId(perm);
const fval = ((x1v && x2v) || x3v) ? 'true' : 'false';
if (accepted) {
setStatus(fmt('✓ Final permutation is the identity → ACCEPT. Formula = %fval%.', { fval: fval }), 'ok');
} else {
setStatus(fmt('✗ Final permutation [%perm%] ≠ identity → REJECT. Formula = %fval%.', { perm: perm.join(' '), fval: fval }), 'bad');
}
});
document.getElementById('reset').addEventListener('click', () => {
['x1','x2','x3'].forEach(id => { document.getElementById(id).checked = false; });
renderEmpty();
setStatus('Set bits and run the program.', 'idle');
});
renderEmpty();
if (!allOk) setStatus('⚠ Internal error — see console.', 'bad');
<p class="hint">This <strong>width-5 permutation branching program</strong> evaluates
<strong>(x₁ ∧ x₂) ∨ x₃</strong> using only permutations of {0,1,2,3,4}.
Toggle the input bits, then click <strong>Run program</strong> to watch the composition
build step by step. The program <em>accepts</em> when the final permutation is the identity.</p>
<div class="inputs">
<label><input type="checkbox" id="x1"> x₁</label>
<label><input type="checkbox" id="x2"> x₂</label>
<label><input type="checkbox" id="x3"> x₃</label>
</div>
<div id="steps-area"></div>
<div class="status" id="status">Set bits and run the program.</div>
<div class="btns">
<button id="run" type="button">Run program</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.inputs { display: flex; gap: 1.4rem; margin-bottom: .9rem; }
.inputs label { display: flex; align-items: center; gap: .4rem; font-weight: 700;
font-size: 1rem; cursor: pointer; user-select: none; }
input[type=checkbox] { width: 18px; height: 18px; cursor: pointer; accent-color: #1d3557; }
#steps-area { display: flex; flex-direction: column; gap: 5px; margin-bottom: .7rem;
max-height: 260px; overflow-y: auto; }
.step { display: grid; grid-template-columns: 100px 60px 20px 1fr;
align-items: center; gap: .4rem; padding: 5px 8px;
border-radius: 7px; background: #edf2f7; border: 1px solid #cdd9e3;
font-size: .83rem; font-family: ui-monospace, monospace; }
.step.accepted { background: #d4edda; border-color: #6abf8a; }
.step.rejected { background: #fde8e8; border-color: #e08b8b; }
.step-label { font-weight: 700; color: #1d3557; }
.step-bit { color: #555; font-size: .8rem; }
.arrow { color: #aaa; text-align: center; }
.perm { display: flex; gap: 3px; }
.perm span { display: inline-flex; align-items: center; justify-content: center;
width: 20px; height: 20px; border-radius: 4px;
background: #dce8f5; font-size: .78rem; font-weight: 600; color: #1d3557; }
.step.accepted .perm span { background: #b8e0c8; color: #0a5c22; }
.step.rejected .perm span { background: #f5c6c6; color: #9b1c1c; }
.status { font-size: .95rem; font-weight: 600; margin: .3rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.idle { color: #555; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Barrington's theorem: NC¹ = width-5 permutation branching programs.
// We implement a VERIFIED 12-step width-5 branching program for (x1 AND x2) OR x3.
//
// Construction via the NOR-commutator technique:
// OR(AND(x1,x2), x3) uses programs P_{NOT-AND} and P_{NOT-x3}:
// [P_{NOT-AND}, P_{NOT-x3}](x) = P_{NOT-AND}(x) * P_{NOT-x3}(x)
// * inv(P_{NOT-AND}(x)) * inv(P_{NOT-x3}(x))
// This equals the identity permutation iff NOT(NOT-AND and NOT-x3) = AND(x1,x2) OR x3.
//
// Permutations of {0,1,2,3,4} (width-5 means 5 states):
// e = [0,1,2,3,4] identity
// a = [1,2,3,4,0] 5-cycle (0 1 2 3 4)
// ai = [4,0,1,2,3] a⁻¹
// b = [1,2,0,3,4] 3-cycle (0 1 2)
// bi = [2,0,1,3,4] b⁻¹
// COMM = a*b*ai*bi = [0,4,1,3,2] commutator [a,b]
// COMMi= COMM⁻¹ = [0,2,4,3,1]
// d = [1,2,3,4,0] = a (used for P_{NOT-x3})
// di = [4,0,1,2,3] = ai
//
// The 12-step program (all perms in S₅, most from A₅):
// Steps 1-5: P_{NOT-AND}(x1,x2) → outputs COMMi iff NAND(x1,x2), e iff AND
// Step 6: P_{NOT-x3}(x3) → outputs a iff x3=0 (NOT x3=1), e iff x3=1
// Steps 7-11: inv(P_{NOT-AND})(x1,x2) → outputs COMM iff NAND, e iff AND
// Step 12: inv(P_{NOT-x3})(x3) → outputs ai iff x3=0, e iff x3=1
//
// This is verified correct for all 8 input combinations (see console).
function cp(p, q) { return p.map(i => q[i]); }
function isId(p) { return p.every((v, i) => v === i); }
const e = [0,1,2,3,4];
const a = [1,2,3,4,0];
const ai = [4,0,1,2,3];
const b = [1,2,0,3,4];
const bi = [2,0,1,3,4];
const COMM = cp(cp(cp(a,b),ai),bi); // = [0,4,1,3,2]
const COMMi= [0,2,4,3,1]; // inv(COMM)
const d = a; // generator for P_{NOT-x3}
const di = ai;
// Each step: [varIdx (0=x1,1=x2,2=x3), perm_if_0, perm_if_1]
// 'null' perm means: use e (identity, regardless of variable).
// 'DUMMY' marker for unconditional steps (same perm for 0 and 1).
const PROG = [
// --- P_{NOT-AND}: 5 steps ---
// 4-step NAND commutator [a,b]: output=e iff NAND(x1,x2)
[0, e, a ], // step 1: x1 → {0:e, 1:a}
[1, e, b ], // step 2: x2 → {0:e, 1:b}
[0, e, ai ], // step 3: x1 → {0:e, 1:ai}
[1, e, bi ], // step 4: x2 → {0:e, 1:bi}
[0, COMMi, COMMi], // step 5: unconditional COMMi → output becomes COMMi iff NAND, e iff AND
// --- P_{NOT-x3}: 1 step ---
[2, d, e ], // step 6: x3 → {0:d=a, 1:e}
// --- inv(P_{NOT-AND}): 5 steps (reverse of P_{NOT-AND} with each perm inverted) ---
[0, COMM, COMM ], // step 7: unconditional COMM (inverse of step 5's COMMi)
[1, e, b ], // step 8: x2 → {0:e, 1:b} (inverse of bi = b, reversed from step 4)
[0, e, a ], // step 9: x1 → {0:e, 1:a} (inverse of ai = a, reversed from step 3)
[1, e, bi ], // step 10: x2 → {0:e, 1:bi} (inverse of b = bi, reversed from step 2)
[0, e, ai ], // step 11: x1 → {0:e, 1:ai} (inverse of a = ai, reversed from step 1)
// --- inv(P_{NOT-x3}): 1 step ---
[2, di, e ], // step 12: x3 → {0:di=ai, 1:e}
];
const VAR_NAMES = ['x₁', 'x₂', 'x₃'];
const SECTION_LABELS = [
'P_NOT-AND step 1', 'P_NOT-AND step 2', 'P_NOT-AND step 3', 'P_NOT-AND step 4',
'P_NOT-AND step 5 (∗)',
'P_NOT-x₃',
'P_NOT-AND⁻¹ step 1 (∗)', 'P_NOT-AND⁻¹ step 2', 'P_NOT-AND⁻¹ step 3',
'P_NOT-AND⁻¹ step 4', 'P_NOT-AND⁻¹ step 5',
'P_NOT-x₃⁻¹',
];
// Verify all 8 truth-table cases at load time
function evalProg(x1v, x2v, x3v) {
const inputs = [x1v, x2v, x3v];
let perm = e.slice();
const trace = [];
for (let i = 0; i < PROG.length; i++) {
const [vi, p0, p1] = PROG[i];
const chosen = inputs[vi] ? p1 : p0;
perm = cp(perm, chosen);
trace.push({ step: i + 1, label: SECTION_LABELS[i], varName: VAR_NAMES[vi],
bitVal: inputs[vi], chosen, after: perm.slice() });
}
return { perm, trace };
}
let allOk = true;
for (let v = 0; v < 8; v++) {
const x1v=(v>>2)&1, x2v=(v>>1)&1, x3v=v&1;
const expected = ((x1v && x2v) || x3v) ? 1 : 0;
const { perm } = evalProg(x1v, x2v, x3v);
const got = isId(perm) ? 1 : 0;
if (expected !== got) {
allOk = false;
console.error(fmt('Verification FAILED: x1=%x1v% x2=%x2v% x3=%x3v% expected=%expected% got=%got%', { x1v: x1v, x2v: x2v, x3v: x3v, expected: expected, got: got }));
}
}
if (allOk) console.log('Barrington program verified correct for all 8 inputs.');
// ── UI ────────────────────────────────────────────────────────────────────────────
function permEls(p) {
return '<div class="perm">' + p.map(v => '<span>'+v+'</span>').join('') + '</div>';
}
function renderEmpty() {
const area = document.getElementById('steps-area');
area.innerHTML = fmt('<div class="step"><span class="step-label">Start</span><span></span><span class="arrow">→</span>%permEls%</div>', { permEls: permEls(e) });
}
function renderTrace(trace) {
const area = document.getElementById('steps-area');
const rows = [fmt('<div class="step"><span class="step-label">Start</span><span></span><span class="arrow">→</span>%permEls%</div>', { permEls: permEls(e) })];
trace.forEach(s => {
const accepted = isId(s.after);
const cls = 'step ' + (accepted ? 'accepted' : 'rejected');
const bit = s.bitVal ? '= 1' : '= 0';
rows.push(
'<div class="'+cls+'"><span class="step-label">'+s.label+'</span>'
+ '<span class="step-bit">'+s.varName+bit+'</span>'
+ '<span class="arrow">→</span>'
+ permEls(s.after)
+ '</div>'
);
});
area.innerHTML = rows.join('');
area.scrollTop = area.scrollHeight;
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t; s.className = 'status ' + cls;
}
document.getElementById('run').addEventListener('click', () => {
const x1v = document.getElementById('x1').checked ? 1 : 0;
const x2v = document.getElementById('x2').checked ? 1 : 0;
const x3v = document.getElementById('x3').checked ? 1 : 0;
const { perm, trace } = evalProg(x1v, x2v, x3v);
renderTrace(trace);
const accepted = isId(perm);
const fval = ((x1v && x2v) || x3v) ? 'true' : 'false';
if (accepted) {
setStatus(fmt('✓ Final permutation is the identity → ACCEPT. Formula = %fval%.', { fval: fval }), 'ok');
} else {
setStatus(fmt('✗ Final permutation [%perm%] ≠ identity → REJECT. Formula = %fval%.', { perm: perm.join(' '), fval: fval }), 'bad');
}
});
document.getElementById('reset').addEventListener('click', () => {
['x1','x2','x3'].forEach(id => { document.getElementById(id).checked = false; });
renderEmpty();
setStatus('Set bits and run the program.', 'idle');
});
renderEmpty();
if (!allOk) setStatus('⚠ Internal error — see console.', 'bad');
Notice that the program never stores any Boolean state — it only tracks which permutation has accumulated so far. Yet by choosing the permutations carefully, the composition encodes the whole formula. This is Barrington's key insight: the non-solvability of S5 provides exactly the algebraic headroom needed to simulate AND with commutator gadgets.
Comments
Loading comments...