The panel below lets you pick a target boolean function and see which Toffoli wiring implements it. Toggle the two input bits, watch all three output wires update live, and verify that the outputs are exactly what the chosen function promises.
<div class="demo-wrap">
<div class="controls">
<label class="ctrl-label">Function to build:</label>
<select id="func-sel">
<option value="nand">NAND(A,B) — fix C=1</option>
<option value="and">AND(A,B) — fix C=0, read NOT-C out</option>
<option value="not">NOT(A) — fix B=1, C=1</option>
<option value="fanout">Fan-out A — fix B=1, C=0</option>
<option value="xor">XOR(A,B) — fix C=0, read C out</option>
<option value="raw">Raw CCNOT — set A, B, C freely</option>
</select>
</div>
<div id="input-row" class="bit-row"></div>
<div class="gate-box">
<svg id="gate-svg" viewBox="0 0 320 140" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
<div id="output-row" class="bit-row out-row"></div>
<div id="info" class="info-box"></div>
<div class="truth-wrap">
<p class="tbl-label">Truth table for this function:</p>
<table id="truth-table" class="truth"></table>
</div>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.demo-wrap { max-width: 540px; margin: 0 auto; padding: .5rem 0; }
.controls { margin-bottom: .7rem; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ctrl-label { font-weight: 600; font-size: .9rem; }
select { font: inherit; padding: .3rem .5rem; border: 1px solid #9ba8b5; border-radius: 6px; background: #f4f7fa; }
.bit-row { display: flex; gap: 1rem; align-items: center; margin: .4rem 0; flex-wrap: wrap; }
.bit-cell { display: flex; flex-direction: column; align-items: center; gap: .25rem; }
.bit-cell .lbl { font-size: .78rem; font-weight: 700; color: #557; letter-spacing: .04em; }
.bit-btn { width: 52px; height: 52px; border-radius: 10px; border: 2px solid #8a9db5;
font: 700 24px ui-monospace, monospace; cursor: pointer; background: #dde3ea;
color: #2a3a55; transition: background .12s, color .12s; }
.bit-btn.one { background: #1d3557; color: #fff; border-color: #1d3557; }
.bit-btn:disabled { cursor: default; opacity: .55; }
.out-row .bit-btn { border-color: #2a7d4f; }
.out-row .bit-btn.one { background: #2a7d4f; border-color: #2a7d4f; }
.gate-box { margin: .4rem 0; }
#gate-svg { width: 100%; max-width: 320px; height: auto; display: block; }
.info-box { font-size: .9rem; background: #f0f5fa; border-left: 3px solid #1d3557;
padding: .5rem .75rem; border-radius: 0 6px 6px 0; margin: .5rem 0; line-height: 1.5; }
.tbl-label { font-weight: 600; font-size: .85rem; margin: .6rem 0 .3rem; }
.truth { border-collapse: collapse; font-size: .85rem; }
.truth th, .truth td { padding: .2rem .55rem; border: 1px solid #c8d4df; text-align: center; }
.truth th { background: #e2eaf3; font-weight: 700; }
.truth tr.active td { background: #d4edda; font-weight: 700; }
// Toffoli (CCNOT) gate: outputs (A, B, A AND B XOR C).
// i.e. A_out=A, B_out=B, C_out = C XOR (A AND B).
// By setting fixed ancilla values we can implement AND, NAND, NOT, fan-out, XOR.
function toffoli(a, b, c) {
return [a, b, c ^ (a & b)];
}
// Config per function: labels for A, B, C inputs; which are free (user toggles); the fixed values for constrained inputs; and a readable formula for the output.
const FUNCS = {
nand: { labels: ['A','B','C=1'], free: [true, true, false], fixed: [null, null, 1],
outLabels: ['A','B','NAND(A,B)'], formula: 'NAND(A,B) = NOT(A AND B)',
note: `C is fixed to 1. C_out = 1 XOR (A AND B) = NAND(A,B).` },
and: { labels: ['A','B','C=0'], free: [true, true, false], fixed: [null, null, 0],
outLabels: ['A','B','AND(A,B)'], formula: 'AND(A,B)',
note: `C is fixed to 0. C_out = 0 XOR (A AND B) = AND(A,B). The highlighted output wire carries the result.` },
not: { labels: ['A','B=1','C=1'], free: [true, false, false], fixed: [null, 1, 1],
outLabels: ['A','B=1','NOT(A)'], formula: 'NOT(A)',
note: `B=1, C=1 fixed. C_out = 1 XOR (A AND 1) = NOT(A).` },
fanout: { labels: ['A','B=1','C=0'], free: [true, false, false], fixed: [null, 1, 0],
outLabels: [`A (copy)`,'B=1',`A (copy)`], formula: `Fan-out: A copies to both A_out and C_out`,
note: `B=1, C=0 fixed. C_out = 0 XOR (A AND 1) = A. Both A_out and C_out carry A.` },
xor: { labels: ['A','B','C=0'], free: [true, true, false], fixed: [null, null, 0],
outLabels: ['A','B','XOR(A,B)'], formula: 'XOR(A,B)',
note: `C fixed to 0. C_out = 0 XOR (A AND B)... wait — that is AND, not XOR! XOR needs two Toffoli steps. Try the NAND and AND modes to see that difference.` },
raw: { labels: ['A','B','C'], free: [true, true, true], fixed: [null, null, null],
outLabels: ['A','B','C XOR (A·B)'], formula: `Raw CCNOT: flip C iff A=1 AND B=1`,
note: `All three inputs are free. Watch C_out = C XOR (A AND B).` },
};
// State
let currentFunc = 'nand';
let inputs = [0, 0, 1]; // A, B, C defaults
function getConfig() { return FUNCS[currentFunc]; }
function computeOutputs() {
const cfg = getConfig();
const a = cfg.free[0] ? inputs[0] : cfg.fixed[0];
const b = cfg.free[1] ? inputs[1] : cfg.fixed[1];
const c = cfg.free[2] ? inputs[2] : cfg.fixed[2];
return toffoli(a, b, c);
}
function buildInputRow() {
const cfg = getConfig();
const row = document.getElementById('input-row');
row.innerHTML = '';
for (let i = 0; i < 3; i++) {
const cell = document.createElement('div');
cell.className = 'bit-cell';
const lbl = document.createElement('div');
lbl.className = 'lbl';
lbl.textContent = cfg.labels[i];
const btn = document.createElement('button');
const val = cfg.free[i] ? inputs[i] : cfg.fixed[i];
btn.className = 'bit-btn' + (val ? ' one' : '');
btn.textContent = val;
btn.disabled = !cfg.free[i];
if (cfg.free[i]) {
const idx = i;
btn.addEventListener('click', () => {
inputs[idx] ^= 1;
refreshAll();
});
}
cell.appendChild(lbl);
cell.appendChild(btn);
row.appendChild(cell);
}
}
function buildOutputRow(outs) {
const cfg = getConfig();
const row = document.getElementById('output-row');
row.innerHTML = '';
for (let i = 0; i < 3; i++) {
const cell = document.createElement('div');
cell.className = 'bit-cell';
const lbl = document.createElement('div');
lbl.className = 'lbl';
lbl.textContent = cfg.outLabels[i];
const btn = document.createElement('button');
btn.className = 'bit-btn' + (outs[i] ? ' one' : '');
btn.textContent = outs[i];
btn.disabled = true;
cell.appendChild(lbl);
cell.appendChild(btn);
row.appendChild(cell);
}
}
function drawGateSVG(outs) {
const cfg = getConfig();
const svg = document.getElementById('gate-svg');
const W = 320, H = 140;
const wireY = [35, 70, 105];
const gateX1 = 110, gateX2 = 210;
const inX = 20, outX = W - 20;
const colors = ['#1a4fc7', '#0a7d33', '#b83000'];
let s = `<svg id="gate-svg" viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg"
fill="none" stroke-width="2" stroke-linecap="round">`;
// Draw wires
for (let i = 0; i < 3; i++) {
s += `<line x1="${inX}" y1="${wireY[i]}" x2="${gateX1}" y2="${wireY[i]}" stroke="${colors[i]}"/>`;
s += `<line x1="${gateX2}" y1="${wireY[i]}" x2="${outX}" y2="${wireY[i]}" stroke="${colors[i]}"/>`;
}
// Gate box
s += `<rect x="${gateX1}" y="18" width="${gateX2 - gateX1}" height="104" rx="8"
fill="#eaf0f8" stroke="#1d3557" stroke-width="1.5"/>`;
// Control dots on A and B wires (inside gate)
s += `<circle cx="${(gateX1+gateX2)/2}" cy="${wireY[0]}" r="5" fill="#1a4fc7" stroke="none"/>`;
s += `<circle cx="${(gateX1+gateX2)/2}" cy="${wireY[1]}" r="5" fill="#0a7d33" stroke="none"/>`;
// Vertical connector from A through B to C (control line)
s += `<line x1="${(gateX1+gateX2)/2}" y1="${wireY[0]}" x2="${(gateX1+gateX2)/2}" y2="${wireY[2]}"
stroke="#666" stroke-width="1.5" stroke-dasharray="4 2"/>`;
// XOR circle on C wire
const mx = (gateX1 + gateX2) / 2, cy3 = wireY[2];
s += `<circle cx="${mx}" cy="${cy3}" r="10" fill="#fff" stroke="#b83000" stroke-width="1.8"/>`;
s += `<line x1="${mx-10}" y1="${cy3}" x2="${mx+10}" y2="${cy3}" stroke="#b83000" stroke-width="1.5"/>`;
s += `<line x1="${mx}" y1="${cy3-10}" x2="${mx}" y2="${cy3+10}" stroke="#b83000" stroke-width="1.5"/>`;
// Input labels
const inVals = [
cfg.free[0] ? inputs[0] : cfg.fixed[0],
cfg.free[1] ? inputs[1] : cfg.fixed[1],
cfg.free[2] ? inputs[2] : cfg.fixed[2],
];
for (let i = 0; i < 3; i++) {
s += `<text x="${inX + 3}" y="${wireY[i] - 7}" fill="${colors[i]}" font-size="11" font-weight="600"
font-family="system-ui">${cfg.labels[i]}=${inVals[i]}</text>`;
s += `<text x="${outX - 3}" y="${wireY[i] - 7}" fill="${colors[i]}" font-size="11" font-weight="600"
font-family="system-ui" text-anchor="end">${cfg.outLabels[i]}=${outs[i]}</text>`;
}
s += '</svg>';
svg.outerHTML = s;
}
function buildInfo() {
const cfg = getConfig();
const el = document.getElementById('info');
el.innerHTML = '<strong>' + cfg.formula + '</strong><br>' + cfg.note;
}
function buildTruthTable() {
const cfg = getConfig();
const tbl = document.getElementById('truth-table');
// Determine which inputs are free
const freeIdxs = cfg.free.map((f, i) => f ? i : null).filter(x => x !== null);
const n = freeIdxs.length;
let hdr = '<tr>';
for (let i = 0; i < 3; i++) {
if (cfg.free[i]) hdr += `<th>${cfg.labels[i]}</th>`;
}
hdr += `<th>${cfg.outLabels[2]}</th></tr>`;
let body = '';
const curVals = [
cfg.free[0] ? inputs[0] : cfg.fixed[0],
cfg.free[1] ? inputs[1] : cfg.fixed[1],
cfg.free[2] ? inputs[2] : cfg.fixed[2],
];
const curOuts = toffoli(curVals[0], curVals[1], curVals[2]);
for (let mask = 0; mask < (1 << n); mask++) {
const row = [
cfg.free[0] ? null : cfg.fixed[0],
cfg.free[1] ? null : cfg.fixed[1],
cfg.free[2] ? null : cfg.fixed[2],
];
for (let bit = 0; bit < n; bit++) {
row[freeIdxs[bit]] = (mask >> (n - 1 - bit)) & 1;
}
const out = toffoli(row[0], row[1], row[2]);
const isActive = row[0] === curVals[0] && row[1] === curVals[1] && row[2] === curVals[2];
body += `<tr${isActive ? ' class="active"' : ''}>`;
for (let i = 0; i < 3; i++) {
if (cfg.free[i]) body += `<td>${row[i]}</td>`;
}
body += `<td>${out[2]}</td></tr>`;
}
tbl.innerHTML = hdr + body;
}
function refreshAll() {
const outs = computeOutputs();
buildInputRow();
buildOutputRow(outs);
drawGateSVG(outs);
buildInfo();
buildTruthTable();
}
// Init
document.getElementById('func-sel').addEventListener('change', function() {
currentFunc = this.value;
// Reset free inputs to 0
inputs = [0, 0, 0];
refreshAll();
});
// Set sensible defaults per function
inputs = [0, 0, 1];
refreshAll();
// Verify in console: CCNOT truth table
console.log(`Toffoli (CCNOT) truth table:`);
for (let a=0;a<2;a++) for (let b=0;b<2;b++) for (let c=0;c<2;c++) {
const [ao,bo,co] = toffoli(a,b,c);
console.log(`A=${a} B=${b} C=${c} -> A=${ao} B=${bo} C=${co} (C_out should be ${c^(a&b)})`);
}
console.log(`NAND check: toffoli(0,0,1)=`, toffoli(0,0,1), '(C_out=1=NAND(0,0))',
'toffoli(1,1,1)=', toffoli(1,1,1), '(C_out=0=NAND(1,1))');
<div class="demo-wrap">
<div class="controls">
<label class="ctrl-label">Function to build:</label>
<select id="func-sel">
<option value="nand">NAND(A,B) — fix C=1</option>
<option value="and">AND(A,B) — fix C=0, read NOT-C out</option>
<option value="not">NOT(A) — fix B=1, C=1</option>
<option value="fanout">Fan-out A — fix B=1, C=0</option>
<option value="xor">XOR(A,B) — fix C=0, read C out</option>
<option value="raw">Raw CCNOT — set A, B, C freely</option>
</select>
</div>
<div id="input-row" class="bit-row"></div>
<div class="gate-box">
<svg id="gate-svg" viewBox="0 0 320 140" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
<div id="output-row" class="bit-row out-row"></div>
<div id="info" class="info-box"></div>
<div class="truth-wrap">
<p class="tbl-label">Truth table for this function:</p>
<table id="truth-table" class="truth"></table>
</div>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.demo-wrap { max-width: 540px; margin: 0 auto; padding: .5rem 0; }
.controls { margin-bottom: .7rem; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ctrl-label { font-weight: 600; font-size: .9rem; }
select { font: inherit; padding: .3rem .5rem; border: 1px solid #9ba8b5; border-radius: 6px; background: #f4f7fa; }
.bit-row { display: flex; gap: 1rem; align-items: center; margin: .4rem 0; flex-wrap: wrap; }
.bit-cell { display: flex; flex-direction: column; align-items: center; gap: .25rem; }
.bit-cell .lbl { font-size: .78rem; font-weight: 700; color: #557; letter-spacing: .04em; }
.bit-btn { width: 52px; height: 52px; border-radius: 10px; border: 2px solid #8a9db5;
font: 700 24px ui-monospace, monospace; cursor: pointer; background: #dde3ea;
color: #2a3a55; transition: background .12s, color .12s; }
.bit-btn.one { background: #1d3557; color: #fff; border-color: #1d3557; }
.bit-btn:disabled { cursor: default; opacity: .55; }
.out-row .bit-btn { border-color: #2a7d4f; }
.out-row .bit-btn.one { background: #2a7d4f; border-color: #2a7d4f; }
.gate-box { margin: .4rem 0; }
#gate-svg { width: 100%; max-width: 320px; height: auto; display: block; }
.info-box { font-size: .9rem; background: #f0f5fa; border-left: 3px solid #1d3557;
padding: .5rem .75rem; border-radius: 0 6px 6px 0; margin: .5rem 0; line-height: 1.5; }
.tbl-label { font-weight: 600; font-size: .85rem; margin: .6rem 0 .3rem; }
.truth { border-collapse: collapse; font-size: .85rem; }
.truth th, .truth td { padding: .2rem .55rem; border: 1px solid #c8d4df; text-align: center; }
.truth th { background: #e2eaf3; font-weight: 700; }
.truth tr.active td { background: #d4edda; font-weight: 700; }
// Toffoli (CCNOT) gate: outputs (A, B, A AND B XOR C).
// i.e. A_out=A, B_out=B, C_out = C XOR (A AND B).
// By setting fixed ancilla values we can implement AND, NAND, NOT, fan-out, XOR.
function toffoli(a, b, c) {
return [a, b, c ^ (a & b)];
}
// Config per function: labels for A, B, C inputs; which are free (user toggles); the fixed values for constrained inputs; and a readable formula for the output.
const FUNCS = {
nand: { labels: ['A','B','C=1'], free: [true, true, false], fixed: [null, null, 1],
outLabels: ['A','B','NAND(A,B)'], formula: 'NAND(A,B) = NOT(A AND B)',
note: `C is fixed to 1. C_out = 1 XOR (A AND B) = NAND(A,B).` },
and: { labels: ['A','B','C=0'], free: [true, true, false], fixed: [null, null, 0],
outLabels: ['A','B','AND(A,B)'], formula: 'AND(A,B)',
note: `C is fixed to 0. C_out = 0 XOR (A AND B) = AND(A,B). The highlighted output wire carries the result.` },
not: { labels: ['A','B=1','C=1'], free: [true, false, false], fixed: [null, 1, 1],
outLabels: ['A','B=1','NOT(A)'], formula: 'NOT(A)',
note: `B=1, C=1 fixed. C_out = 1 XOR (A AND 1) = NOT(A).` },
fanout: { labels: ['A','B=1','C=0'], free: [true, false, false], fixed: [null, 1, 0],
outLabels: [`A (copy)`,'B=1',`A (copy)`], formula: `Fan-out: A copies to both A_out and C_out`,
note: `B=1, C=0 fixed. C_out = 0 XOR (A AND 1) = A. Both A_out and C_out carry A.` },
xor: { labels: ['A','B','C=0'], free: [true, true, false], fixed: [null, null, 0],
outLabels: ['A','B','XOR(A,B)'], formula: 'XOR(A,B)',
note: `C fixed to 0. C_out = 0 XOR (A AND B)... wait — that is AND, not XOR! XOR needs two Toffoli steps. Try the NAND and AND modes to see that difference.` },
raw: { labels: ['A','B','C'], free: [true, true, true], fixed: [null, null, null],
outLabels: ['A','B','C XOR (A·B)'], formula: `Raw CCNOT: flip C iff A=1 AND B=1`,
note: `All three inputs are free. Watch C_out = C XOR (A AND B).` },
};
// State
let currentFunc = 'nand';
let inputs = [0, 0, 1]; // A, B, C defaults
function getConfig() { return FUNCS[currentFunc]; }
function computeOutputs() {
const cfg = getConfig();
const a = cfg.free[0] ? inputs[0] : cfg.fixed[0];
const b = cfg.free[1] ? inputs[1] : cfg.fixed[1];
const c = cfg.free[2] ? inputs[2] : cfg.fixed[2];
return toffoli(a, b, c);
}
function buildInputRow() {
const cfg = getConfig();
const row = document.getElementById('input-row');
row.innerHTML = '';
for (let i = 0; i < 3; i++) {
const cell = document.createElement('div');
cell.className = 'bit-cell';
const lbl = document.createElement('div');
lbl.className = 'lbl';
lbl.textContent = cfg.labels[i];
const btn = document.createElement('button');
const val = cfg.free[i] ? inputs[i] : cfg.fixed[i];
btn.className = 'bit-btn' + (val ? ' one' : '');
btn.textContent = val;
btn.disabled = !cfg.free[i];
if (cfg.free[i]) {
const idx = i;
btn.addEventListener('click', () => {
inputs[idx] ^= 1;
refreshAll();
});
}
cell.appendChild(lbl);
cell.appendChild(btn);
row.appendChild(cell);
}
}
function buildOutputRow(outs) {
const cfg = getConfig();
const row = document.getElementById('output-row');
row.innerHTML = '';
for (let i = 0; i < 3; i++) {
const cell = document.createElement('div');
cell.className = 'bit-cell';
const lbl = document.createElement('div');
lbl.className = 'lbl';
lbl.textContent = cfg.outLabels[i];
const btn = document.createElement('button');
btn.className = 'bit-btn' + (outs[i] ? ' one' : '');
btn.textContent = outs[i];
btn.disabled = true;
cell.appendChild(lbl);
cell.appendChild(btn);
row.appendChild(cell);
}
}
function drawGateSVG(outs) {
const cfg = getConfig();
const svg = document.getElementById('gate-svg');
const W = 320, H = 140;
const wireY = [35, 70, 105];
const gateX1 = 110, gateX2 = 210;
const inX = 20, outX = W - 20;
const colors = ['#1a4fc7', '#0a7d33', '#b83000'];
let s = `<svg id="gate-svg" viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg"
fill="none" stroke-width="2" stroke-linecap="round">`;
// Draw wires
for (let i = 0; i < 3; i++) {
s += `<line x1="${inX}" y1="${wireY[i]}" x2="${gateX1}" y2="${wireY[i]}" stroke="${colors[i]}"/>`;
s += `<line x1="${gateX2}" y1="${wireY[i]}" x2="${outX}" y2="${wireY[i]}" stroke="${colors[i]}"/>`;
}
// Gate box
s += `<rect x="${gateX1}" y="18" width="${gateX2 - gateX1}" height="104" rx="8"
fill="#eaf0f8" stroke="#1d3557" stroke-width="1.5"/>`;
// Control dots on A and B wires (inside gate)
s += `<circle cx="${(gateX1+gateX2)/2}" cy="${wireY[0]}" r="5" fill="#1a4fc7" stroke="none"/>`;
s += `<circle cx="${(gateX1+gateX2)/2}" cy="${wireY[1]}" r="5" fill="#0a7d33" stroke="none"/>`;
// Vertical connector from A through B to C (control line)
s += `<line x1="${(gateX1+gateX2)/2}" y1="${wireY[0]}" x2="${(gateX1+gateX2)/2}" y2="${wireY[2]}"
stroke="#666" stroke-width="1.5" stroke-dasharray="4 2"/>`;
// XOR circle on C wire
const mx = (gateX1 + gateX2) / 2, cy3 = wireY[2];
s += `<circle cx="${mx}" cy="${cy3}" r="10" fill="#fff" stroke="#b83000" stroke-width="1.8"/>`;
s += `<line x1="${mx-10}" y1="${cy3}" x2="${mx+10}" y2="${cy3}" stroke="#b83000" stroke-width="1.5"/>`;
s += `<line x1="${mx}" y1="${cy3-10}" x2="${mx}" y2="${cy3+10}" stroke="#b83000" stroke-width="1.5"/>`;
// Input labels
const inVals = [
cfg.free[0] ? inputs[0] : cfg.fixed[0],
cfg.free[1] ? inputs[1] : cfg.fixed[1],
cfg.free[2] ? inputs[2] : cfg.fixed[2],
];
for (let i = 0; i < 3; i++) {
s += `<text x="${inX + 3}" y="${wireY[i] - 7}" fill="${colors[i]}" font-size="11" font-weight="600"
font-family="system-ui">${cfg.labels[i]}=${inVals[i]}</text>`;
s += `<text x="${outX - 3}" y="${wireY[i] - 7}" fill="${colors[i]}" font-size="11" font-weight="600"
font-family="system-ui" text-anchor="end">${cfg.outLabels[i]}=${outs[i]}</text>`;
}
s += '</svg>';
svg.outerHTML = s;
}
function buildInfo() {
const cfg = getConfig();
const el = document.getElementById('info');
el.innerHTML = '<strong>' + cfg.formula + '</strong><br>' + cfg.note;
}
function buildTruthTable() {
const cfg = getConfig();
const tbl = document.getElementById('truth-table');
// Determine which inputs are free
const freeIdxs = cfg.free.map((f, i) => f ? i : null).filter(x => x !== null);
const n = freeIdxs.length;
let hdr = '<tr>';
for (let i = 0; i < 3; i++) {
if (cfg.free[i]) hdr += `<th>${cfg.labels[i]}</th>`;
}
hdr += `<th>${cfg.outLabels[2]}</th></tr>`;
let body = '';
const curVals = [
cfg.free[0] ? inputs[0] : cfg.fixed[0],
cfg.free[1] ? inputs[1] : cfg.fixed[1],
cfg.free[2] ? inputs[2] : cfg.fixed[2],
];
const curOuts = toffoli(curVals[0], curVals[1], curVals[2]);
for (let mask = 0; mask < (1 << n); mask++) {
const row = [
cfg.free[0] ? null : cfg.fixed[0],
cfg.free[1] ? null : cfg.fixed[1],
cfg.free[2] ? null : cfg.fixed[2],
];
for (let bit = 0; bit < n; bit++) {
row[freeIdxs[bit]] = (mask >> (n - 1 - bit)) & 1;
}
const out = toffoli(row[0], row[1], row[2]);
const isActive = row[0] === curVals[0] && row[1] === curVals[1] && row[2] === curVals[2];
body += `<tr${isActive ? ' class="active"' : ''}>`;
for (let i = 0; i < 3; i++) {
if (cfg.free[i]) body += `<td>${row[i]}</td>`;
}
body += `<td>${out[2]}</td></tr>`;
}
tbl.innerHTML = hdr + body;
}
function refreshAll() {
const outs = computeOutputs();
buildInputRow();
buildOutputRow(outs);
drawGateSVG(outs);
buildInfo();
buildTruthTable();
}
// Init
document.getElementById('func-sel').addEventListener('change', function() {
currentFunc = this.value;
// Reset free inputs to 0
inputs = [0, 0, 0];
refreshAll();
});
// Set sensible defaults per function
inputs = [0, 0, 1];
refreshAll();
// Verify in console: CCNOT truth table
console.log(`Toffoli (CCNOT) truth table:`);
for (let a=0;a<2;a++) for (let b=0;b<2;b++) for (let c=0;c<2;c++) {
const [ao,bo,co] = toffoli(a,b,c);
console.log(`A=${a} B=${b} C=${c} -> A=${ao} B=${bo} C=${co} (C_out should be ${c^(a&b)})`);
}
console.log(`NAND check: toffoli(0,0,1)=`, toffoli(0,0,1), '(C_out=1=NAND(0,0))',
'toffoli(1,1,1)=', toffoli(1,1,1), '(C_out=0=NAND(1,1))');
Comments
Loading comments...