<<ProdiamsLogic`

(* functions *)

(* evaluates the expression with symbols replaced with actual values *)
evaluate[expression_,symbols_,values_]:=expression/.Thread[symbols->values]

(* adds the results column to the table of argument tuples *)
appendAllResults[arguments_,results_]:=Map[
Function[argResPair,Apply[Join,argResPair]],
Transpose[{arguments,Map[List,results]}]
]

(* generates a new term like A&&B&&!C from symbols and their values *)
makeTerm[symbols_,args_]:=And@@MapThread[
Function[{sym,val},
If[val,sym,!sym]
],
{symbols,args}
]

(* generates a truth table for the given expression,
fullValues allows to use short forms T/F instead of True/False and
resultFunction allows to provide custom function for computing results in the result column *)
truthTable[expression_,OptionsPattern[{fullValues->True,resultFunction->evaluate}]]:=Module[
{
symbols=BooleanVariables[expression],
numOfSymbols,
parameterValueTable,
results,
finalTable,
fullVals=OptionValue[fullValues],
resFun=OptionValue[resultFunction]
},

numOfSymbols=Length[symbols];

(* values for parameter symbols: *)
parameterValueTable=Tuples[{False,True},numOfSymbols];

results=Map[
Function[args,
resFun[expression,symbols,args]
],
parameterValueTable
];

finalTable=Join[
{
(* header row *)
Join[
Map[TraditionalForm,symbols],
{"result"}
]
},

(* truth values *)
appendAllResults[
Map[ToString,parameterValueTable,{2}], (* arguments as strings *)
results
]
];

If[fullVals,
finalTable,
finalTable/.{"True"->"T","False"->"F"}
]
]

(* generates the truth table and wraps it in the grid *)
truthGrid[expression_,OptionsPattern[{fullValues->False,resultFunction->evaluate}]]:=With[
{
tTable=truthTable[expression,
fullValues->OptionValue[fullValues],
resultFunction->OptionValue[resultFunction]
]
},

Grid[tTable,Frame->All]
]

(* logic assistant GUI *)

expression=ToExpression["A&&B"];
outputFormat=CForm;
drawDiagram=True;

formsExplained=<|
"DNF"->"disjunctive normal form (sum of products)",
"CNF"->"conjunctive normal form (product of sums)",
"ANF"->"algebraic normal form",
"NOR"->"two-level Nor and Not",
"NAND"->"two-level Nand and Not",
"AND"->"two-level And and Not",
"OR"->"two-level Or and Not",
"BDT"->"Boolean decision tree"
|>;

explainForm[form_]:=If[KeyExistsQ[formsExplained,form],
Tooltip[form,formsExplained[form]],
form
]

formatButton[expr_,format_]:=Button[
Tooltip[Format[expr,format],"Continue with this form"],
expression=expr,
Appearance->"Frameless"
]

editTermButton[expr_,symbols_,values_]:=With[
{
res=evaluate[expr,symbols,values],
term=makeTerm[symbols,values]
},

Button[
(* label *)
Tooltip[ToString[res],If[res,"Remove this term","Add this term"]],

(* action *)
If[res,
expression=And[expression,Not[term]],(* remove term from expression *)
expression=Or[expression,term] (* add term to expression *)
];
(*expression=BooleanMinimize[expression]*)
(* Note: doConvert here would recompute the new expression,
but with the risk of losing some symbols in the resulting truth table,
as some symbols can be eliminated by BooleanMinimize,
it is better to allow the user do it explicitly with the Convert button *)
doConvert[],

(* options *)
Appearance->"Frameless"
]
]

doConvert[]:=Module[
{
diagram,
convertedFormsGrid,
editableTruthsGrid,
diagramCells,
altFormCells,
truthTableCells
},

If[drawDiagram,
diagram=logicDiagram[expression],
diagram=""
];

convertedFormsGrid=Grid[
Table[
{explainForm[form],formatButton[BooleanConvert[expression,form],outputFormat]},{form,{"DNF","CNF","ANF","NOR","NAND","AND","OR","IMPLIES","IF",(*"BFF",*)"BDT"}}
],
Frame->All
];

editableTruthsGrid=truthGrid[expression,
fullValues->True,
resultFunction->Function[{e,s,v},editTermButton[e,s,v]]
];

diagramCells=Cells[ButtonNotebook[],CellTags->"diagram"];
If[diagramCells=={},
If[drawDiagram,Print["no result cells to show diagram"]],
Map[
Function[resultCell,
NotebookWrite[resultCell,
Cell[BoxData[ToBoxes[diagram]],"Output",ShowCellBracket->False,CellTags->"diagram"]
]
],
diagramCells
]
];

altFormCells=Cells[ButtonNotebook[],CellTags->"altForms"];
If[altFormCells=={},
Print["no result cells for alternative forms"],
Map[
Function[resultCell,
NotebookWrite[resultCell,
Cell[BoxData[ToBoxes[convertedFormsGrid]],"Output",ShowCellBracket->False,CellTags->"altForms"]
]
],
altFormCells
]
];

truthTableCells=Cells[ButtonNotebook[],CellTags->"truthTable"];
If[truthTableCells=={},
Print["no result cells for truth table"],
Map[
Function[truthTableCell,
NotebookWrite[truthTableCell,
Cell[BoxData[ToBoxes[editableTruthsGrid]],"Output",ShowCellBracket->False,CellTags->"truthTable"]
]
],
truthTableCells
]
];
]

Panel[
Column[{
InputField[Dynamic[expression],ImageSize->500],
Row[{
RadioButton[Dynamic[outputFormat],CForm]," C    ",
RadioButton[Dynamic[outputFormat],StandardForm]," Standard    ",
RadioButton[Dynamic[outputFormat],TraditionalForm]," Traditional    ",
RadioButton[Dynamic[outputFormat],InputForm]," Input    ",
RadioButton[Dynamic[outputFormat],TeXForm]," TeX"
}],
Row[{Checkbox[Dynamic[drawDiagram]]," draw diagram"}],
Button["Convert",doConvert[]]
}],
"Logic Assistant",
Appearance->"Frameless",
FrameMargins->10
]

Diagram:

Alternative forms:

Truth table: