Public
Edited
Mar 30, 2023
3 stars
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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more