% The	luatruthtable	package
% Authors:  Chetan  Shirore  and  Ajit  Kumar
% Version 1.3, Date=17-Nov-2023
% Licensed under LaTeX Project Public License v1.3c or later. The complete license text is available at http://www.latex-project.org/lppl.txt.

\ProvidesPackage{luatruthtable}[1.3]
\RequirePackage{xkeyval}
\RequirePackage{amsmath}
\RequirePackage{luacode}
\begin{luacode*}
-- Function to convert decimal number to binary number.
local function toBinary(x,y)
    y = y or math.max(1, select(2, math.frexp(x)))
    local res = {}
    for i = y, 1, -1 do
        res[i] = math.fmod(x, 2)
        x = math.floor((x - res[i]) / 2)
    end
    return res
end
-- Define logical operators.
local function _not(a)
  if a ==0 then return 1
    else return 0 end
end

local lognot = {}

local not_mt= {
  __mul = function(a,b)
      return _not(b)

  end
}

setmetatable(lognot, not_mt)
_G.lognot = lognot

local function _and(a, b)
  if a == 1 and b == 1 then return 1
    else return 0 end
end

local tmp1 = {}
local logand = {}
local v1 = 0

local and_mt= {
  __mul = function(a,b)
    if b==logand then
    	v1 = v1 + 1 
      tmp1[v1]=a
      return tmp1
    elseif a == tmp1 then
      local w1 = _and(tmp1[v1], b)
      v1 = v1 - 1
		return w1
    end
  end
}

setmetatable(tmp1, and_mt)
setmetatable(logand, and_mt)
_G.logand = logand

local function _or(a, b)
  if a == 0 and b == 0 then return 0
    else return 1 end
end

local tmp2 = {}
local logor = {}
local v2 = 0

local or_mt= {
  __mul = function(a,b)
    if b==logor then
    	 v2 = v2 + 1 
      tmp2[v2] = a
      return tmp2
    elseif a == tmp2 then
      local w2 =  _or(tmp2[v2], b)
      v2 = v2 - 1
		return w2
    end
  end
}

setmetatable(tmp2, or_mt)
setmetatable(logor, or_mt)
_G.logor = logor

local function _imp(a, b)
  if a == 1 and b == 0 then return 0
    else return 1 end
end

local tmp3 = {}
local imp = {}
local v3 = 0

local imp_mt= {
  __mul = function(a,b)
    if b==imp then
		 v3 = v3 + 1
      tmp3[v3]=a
      return tmp3
    elseif a == tmp3 then
      local w3 =  _imp(tmp3[v3], b)
      v3 = v3 - 1
		return w3
    end
  end
}

setmetatable(tmp3, imp_mt)
setmetatable(imp, imp_mt)
_G.imp = imp

local function _iff(a, b)
  if a == b then return 1
    else return 0 end
end

local tmp4 = {}
local iff = {}
local v4 = 0

local iff_mt= {
  __mul = function(a,b)
    if b==iff then
     v4 = v4 + 1 
      tmp4[v4]=a
      return tmp4
    elseif a == tmp4 then
      local w4 = _iff(tmp4[v4], b)
      v4 = v4 - 1
		return w4
    end
  end
}

setmetatable(tmp4, iff_mt)
setmetatable(iff, iff_mt)
_G.iff = iff

local function _xor(a, b)
  if a ~= b then return 1
    else return 0 end
end

local tmp5 = {}
local logxor = {}
local v5 = 0

local xor_mt= {
  __mul = function(a,b)
    if b==logxor then
     v5 = v5 + 1 
      tmp5[v5]=a
      return tmp5
    elseif a == tmp5 then
      local w5 = _xor(tmp5[v5], b)
      v5 = v5 - 1
		return w5
    end
  end
}

setmetatable(tmp5, xor_mt)
setmetatable(logxor, xor_mt)
_G.logxor = logxor

local function _nand(a, b)
  if a ==1 and b == 1 then return 0
    else return 1 end
end

local tmp6 = {}
local lognand = {}
local v6 = 0

local nand_mt= {
  __mul = function(a,b)
    if b==lognand then
		 v6 = v6 + 1 
      tmp6[v6]=a
      return tmp6
    elseif a == tmp6 then
      local w6 =  _nand(tmp6[v6], b)
      v6 = v6 - 1
		return w6
    end
  end
}

setmetatable(tmp6, nand_mt)
setmetatable(lognand, nand_mt)
_G.lognand = lognand

local function _nor(a, b)
  if a ==0 and b == 0 then return 1
    else return 0 end
end

local tmp7 = {}
local lognor = {}
local v7 = 0

local nor_mt= {
  __mul = function(a,b)
    if b==lognor then
	   v7 = v7 + 1 
      tmp7[v7]=a
      return tmp7
    elseif a == tmp7 then
      local w7 = _nor(tmp7[v7], b)
		 v7 = v7 - 1
		return w7
    end
  end
}

setmetatable(tmp7, nor_mt)
setmetatable(lognor, nor_mt)
_G.lognor = lognor

local function _xnor(a, b)
  if a == b then return 1
    else return 0 end
end

local tmp8 = {}
local logxnor = {}
local v8 = 0

local xnor_mt= {
  __mul = function(a,b)
    if b==logxnor then
      v8 = v8 + 1 
      tmp8[v8]=a
      return tmp8
    elseif a == tmp8 then
     local w8 =  _xnor(tmp8[v8], b)
     v8 = v8 - 1
		return w8
    end
  end
}

setmetatable(tmp8, xnor_mt)
setmetatable(logxnor, xnor_mt)
_G.logxnor = logxnor

function truthTable(str0,str,trtext,fltext)
local eval=""
local names={lognot=lognot,logand=logand,logor=logor, imp=imp, 
  iff=iff, logxor=logxor,lognand=lognand,lognor=lognor, logxnor=logxnor}
local vars={}
local expr={}
local countexp = 1
local countvars =1
local res=""
local sep=" & "
trtext = trtext or "$T$"
fltext = fltext or "$F$"
local str0 = string.gsub(str0, "%s+", "")
local str = string.gsub(str, "%s+", "")
for variables in string.gmatch(str0, '([^,]+)') do
  vars[countvars] = variables
 countvars = countvars + 1
end

for subexp in string.gmatch(str, '([^,]+)') do
    expr[countexp] = subexp
    countexp = countexp + 1
end

local n =#vars
for i =1,2^n do
  itr = toBinary(i,n)
  for j=1, #itr do
    names[vars[j]] = itr[j]
end
    for k = 1, #expr do
      res= res ..sep.. load("return " .. expr[k],nil,"t",names)()
      end
if i~=2^n then
eval = table.concat(toBinary(i,n)," & ") .. res .."\\\\" ..eval
else
eval = eval .. table.concat(toBinary(i,n)," & ") .. res
end
 res=""
end

if trtext:gsub("%s+", "") =="0" or trtext:gsub("%s+", "") == "$0$"  
or fltext:gsub("%s+", "") =="1" or fltext:gsub("%s+", "") == "$1$" 
then
eval=eval
else
eval=eval:gsub(0,fltext):gsub(1,trtext)
end
return eval
end

\end{luacode*}

% ========= KEY DEFINITIONS =========
\define@key{luatruthtable}{trtext}{\def\luatrtbl@trtext{#1}}%
\define@key{luatruthtable}{fltext}{\def\luatrtbl@fltext{#1}}%

% ========= KEY DEFAULTS =========
\setkeys{luatruthtable}{trtext=$T$,fltext=$F$}%
% ========= Defining Command =========
\newcommand{\luaTruthTable}[3][]{%
  \setkeys{luatruthtable}{#1}%
  \directlua{tex.sprint(truthTable(\luastringN{#2},\luastringN{#3},'\luatrtbl@trtext','\luatrtbl@fltext'))}} %

\endinput
