Public
Edited
Mar 30, 2023
3 stars
Also listed in…
Math
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
truthTableFunction([1, 1, 1, 1])("anything->q")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
trueTruthTable = truthTableFunction()
Insert cell
function truth_slider(value, label) {
let seqs = [[0], [1]];
for (let i = 0; i < 3; i++) {
seqs = seqs.map((a) => [a.concat([0]), a.concat([1])]).flat();
}

let bits = d3.create("div");
let bitElem = bits.node();
bitElem.value = value;

let cur_value = seqs.map((c) => test(c, value)).indexOf(true);
bits.append("div").style("font-weight", "bold").html(label);
let step = bits.append("button").text("^");
let slider = bits
.append("input")
.attr("type", "range")
.attr("min", 0)
.attr("max", 15)
.attr("step", 1)
.attr("value", cur_value)
.style("margin-left", "10px");
let bit_display = bits
.append("div")
.style("display", "inline-block")
.style("margin-left", "10px")
.html(`[${value.map((t) => (t ? "T" : "F"))}]`);

slider.on("input", function () {
cur_value = parseInt(this.value);
bitElem.value = seqs[cur_value];
bit_display.html(`[${seqs[cur_value].map((d) => (d == 1 ? "T" : "F"))}]`);
bitElem.dispatchEvent(new CustomEvent("input"));
});

step.on("click", function () {
cur_value = (parseInt(slider.node().value) + 1) % 16;
slider.node().value = cur_value;
bitElem.value = seqs[cur_value];
bit_display.html(`[${seqs[cur_value].map((d) => (d == 1 ? "T" : "F"))}]`);
bitElem.dispatchEvent(new CustomEvent("input"));
});

return bitElem;

function test(a, b) {
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
}
}
Insert cell
<style>
th:first-child input, td:first-child input{
display: none;
}
th {
pointer-events: none;
}
</style>
Insert cell
Insert cell
function truthTableFunction(bits = [1, 0, 1, 1]) {
function truthTable(exp) {
const data = [];
const sub = [];
const f = logicParser(exp);
const text = textFormula(f);
const subText = {};
const subPush = (f) => {
if (subText.hasOwnProperty(f.text)) return;
sub.push(f);
subText[f.text] = true;
};
function trav(f) {
if (f.node === "Literal") {
subPush(f);
return;
}
if (f.node === "Not") {
trav(f.child[0]);
subPush(f);
return;
}
trav(f.child[0]);
trav(f.child[1]);
subPush(f);
}
trav(f);
const alignment = {};
sub.forEach((f) => (alignment[f.text] = "center"));
let vars = variables(f);
for (let v of values(vars)) {
const row = {};
for (let i = 0; i < sub.length; ++i) {
row[sub[i].text] = evalFormula(sub[i], v) ? "T" : "F";
}
data.push(row);
}
return Inputs.table(data, { layout: "auto", align: alignment });
}

function textFormula(f) {
const precedence = {
Or: 2,
And: 2,
Implies: 3,
WeakImply: 3,
Not: 1,
Literal: 0
};
const paren = (f, op) =>
precedence[f.node] >= precedence[op]
? "(" + textFormula(f) + ")"
: textFormula(f);
if (f.text === undefined) {
switch (f.node) {
case "Literal":
f.text = f.value;
break;
case "Or":
f.text = paren(f.child[0], "Or") + "∨" + paren(f.child[1], "Or");
break;
case "Xor":
f.text = paren(f.child[0], "Xor") + " ⊕" + paren(f.child[1], "Xor");
break;
case "And":
f.text = paren(f.child[0], "And") + "∧" + paren(f.child[1], "And");
break;
case "Implies":
f.text =
paren(f.child[0], "Implies") + "⇒" + paren(f.child[1], "Implies");
break;
case "Equiv":
f.text =
paren(f.child[0], "Equiv") + "⇔" + paren(f.child[1], "Equiv");
break;
case "Not":
f.text = "¬" + paren(f.child[0], "Not");
break;
case "WeakImply":
f.text =
paren(f.child[0], "WeakImply") +
"→" +
paren(f.child[1], "WeakImply");
break;
}
}
return f.text;
}
function* values(vars) {
const n = vars.length;
for (let i = 0; i < 2 ** n; ++i) {
let vl = {};
let b = 1;
for (let j = n - 1; j >= 0; --j, b *= 2) {
vl[vars[j]] = (b & i) == 0;
}
yield vl;
}
}

function variables(f) {
const v = {};
function vr(f) {
if (f.node === "Literal") {
v[f.value] = true;
return;
}
f.child.forEach((c) => vr(c));
}
vr(f, v);
return Object.keys(v).sort();
}
function evalFormula(f, v) {
if (typeof f === "string") f = logicParser(f);
function ef(f) {
if (f.node === "Literal") return v[f.value];
if (f.node === "And") return ef(f.child[0]) && ef(f.child[1]);
if (f.node === "Or") return ef(f.child[0]) || ef(f.child[1]);
if (f.node === "Implies") return !ef(f.child[0]) || ef(f.child[1]);
if (f.node === "Equiv") return ef(f.child[0]) === ef(f.child[1]);
if (f.node === "Xor") {
const a = ef(f.child[0]);
const b = ef(f.child[1]);
return (a || b) && !(a && b);
}
if (f.node === "Not") return !ef(f.child[0]);
if (f.node === "WeakImply") {
return explicit_formula(...bits)(ef(f.child[0]), ef(f.child[1]));
}
}
return ef(f);
}

const expParserData = {
tokens: {
Space: /\s+/,
Literal: /\w+/,
Or: [/∨/, /\|/],
And: [/∧/, /&/],
Implies: [/⇒/, /=>/],
WeakImply: [/→/, /->/],
Equiv: [/⇔/, /<=>/],
Xor: [/⊕/, /\^/],
Not: [/¬/, /\!/],
LeftParen: /\(/,
RightParen: /\)/
},
binaryOp: ["Or", "And", "Implies", "WeakImply", "Equiv", "Xor"],
precedence: {
Or: 3,
And: 3,
Xor: 3,
Implies: 2,
WeakImply: 2,
Not: 4,
Equiv: 1
},
associativity: { Implies: "Right", Equiv: "Right", WeakImply: "Right" },
unaryOp: ["Not"],
terminal: ["Literal"]
};

let logicParser = expressionParser(expParserData);

function tokenizer(lex) {
const lexa = Object.entries(lex);
lex = [];
lexa.forEach(([a, b]) => {
if (b instanceof RegExp)
lex.push([new RegExp("^(" + b.source + ")(.*)"), a]);
else
b.forEach((t) => lex.push([new RegExp("^(" + t.source + ")(.*)"), a]));
});
return (str) => {
let s = str;
const r = [];
while (s) {
let flag = true;
for (let i = 0; i < lex.length; ++i) {
let m = s.match(lex[i][0]);
if (m) {
const l = lex[i][1];
r.push([l, m[1]]);
s = m[2];
flag = false;
break;
}
}
if (flag) throw "SyntaxError: invalid token";
}
r.push(["End", ""]);
return r;
};
}

function expressionParser(data) {
const to = tokenizer(data.tokens);
const binaryOp = {};
data.binaryOp.forEach((op) => (binaryOp[op] = true));
const isBinary = (tok) => binaryOp.hasOwnProperty(tok);
const unaryOp = {};
data.unaryOp.forEach((op) => (unaryOp[op] = true));
const isUnary = (tok) => unaryOp.hasOwnProperty(tok);
const terminal = {};
data.terminal.forEach((op) => (terminal[op] = true));
const isTerminal = (tok) => terminal.hasOwnProperty(tok);
const prec = (tok) => data.precedence[tok];
const associativity = (tok) =>
!data.associativity.hasOwnProperty(tok)
? "Left"
: data.associativity[tok];
return function (str) {
const l = to(str).filter(([tok, _]) => tok !== "Space");
let it = 0;
const next = () => l[it][0];
const value = () => l[it][1];
const consume = () => (it = it + 1);
const expect = (tok) => {
if (next() === tok) consume();
else throw "SyntaxError: unexpected token";
};
let Eparser, Exp, P;
const binary = (tok) => {
return tok;
};
const unary = (tok) => {
if (isBinary(tok)) return "Unary" + tok;
else return tok;
};
Eparser = () => {
const t = Exp(0);
expect("End");
return t;
};
Exp = (p) => {
let t = P();
while (isBinary(next()) && prec(binary(next())) >= p) {
const op = binary(next());
consume();
const q = prec(op) + (associativity(op) === "Right" ? 0 : 1);
const t1 = Exp(q);
t = { node: op, child: [t, t1] };
}
return t;
};
P = () => {
if (isUnary(next())) {
const op = unary(next());
consume();
const q = prec(op);
const t = Exp(q);
return { node: op, child: [t] };
} else if (next() === "LeftParen") {
consume();
const t = Exp(0);
expect("RightParen");
return t;
} else if (isTerminal(next())) {
const t = { node: next(), value: value() };
consume();
return t;
} else throw "SyntaxError: ";
};
return Eparser();
};
}

function explicit_formula(z1, z2, z3, z4) {
let f = (p, q) => {
if (p && q) {
return Boolean(z1);
} else if (p & !q) {
return Boolean(z2);
} else if (!p & q) {
return Boolean(z3);
} else if (!p & !q) {
return Boolean(z4);
}
};
return f;
}

return truthTable;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more