from sympy.logic.boolalg import *
from sympy.logic import simplify_logic
from sympy import symbols

c = 0
t = 1
k = 2

u    = symbols('u')
u1    = symbols('u1')
u2    = symbols('u2')
r    = symbols('r')
R    = symbols('R')

x = [0,0,0]
z = [0,0,0]
x[k] = symbols("x[k]")
x[c] = symbols("x[c]")
x[t] = symbols("x[t]")
z[k] = symbols("z[k]")
z[c] = symbols("z[c]")
z[t] = symbols("z[t]")

X    = symbols('X')
Z    = symbols('Z')

def strstrip(item1):
    return item1.lstrip(' ()~')

def to_py(s):
    for a in s.split("&"):
        print("    cnf.add_clause([", end="")
        l = a.split("|")
        l.sort(key=strstrip)
        for x in l:
            b = x.strip(' ()')
            if b.startswith('~'):
                b = b.replace('~', '-')
            else:
                b = " "+ b
            print(b, end="")
            if x != l[-1]:
                print(", ", end="")
        print("])")

def main():

    print("# This code was generated by cliffordt2cnf_py_codegen.py.")
    print()

    # H:
    F = Equivalent(R, r ^ (x[k] & z[k]) )

    C = to_cnf(F, True, True)
    print("# "+ str(F))
    print("def H2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    to_py(	   str(C))
    print("    x[k], z[k] = z[k], x[k]")
    print()
    print("    cnf.vars.r = R")
    print()

    # S:
    Fr = Equivalent(R, (r ^ x[k] & z[k] ))
    Cr = to_cnf(Fr, True, True)

    Fz = Equivalent(Z, x[k] ^ z[k])
    Cz = to_cnf(Fz, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fz))
    print("def S2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    print("    Z = cnf.add_var()")
    to_py(	   str(Cz))
    print()
    print("    cnf.vars.z[k] = Z")
    print("    cnf.vars.r = R")
    print()

    # X:
    Fr = Equivalent(R, (r ^ z[k]))
    Cr = to_cnf(Fr, True, True)

    print("# "+ str(Fr))
    print("def X2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    print("    cnf.vars.r = R")
    print()

    # Y:
    Fr = Equivalent(R, (r ^ x[k] ^ z[k]))
    Cr = to_cnf(Fr, True, True)

    print("# "+ str(Fr))
    print("def Y2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    print("    cnf.vars.r = R")
    print()

    # Z:
    Fr = Equivalent(R, (r ^ x[k]))
    Cr = to_cnf(Fr, True, True)

    print("# "+ str(Fr))
    print("def Z2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    R = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    print("    cnf.vars.r = R")
    print()

    #Sdg
    Fr = Equivalent(R, (r ^ x[k] & ~z[k]))
    Cr = to_cnf(Fr, True, True)

    Fz = Equivalent(Z, x[k] ^ z[k])
    Cz = to_cnf(Fz, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fz))
    print("def Sdg2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    print("    Z = cnf.add_var()")
    to_py(	   str(Cz))
    print()
    print("    cnf.vars.z[k] = Z")
    print("    cnf.vars.r = R")
    print()    
    
    
    #T
    Fr = Equivalent(R, r ^ (x[k] & z[k] & ~Z))
    Cr = to_cnf(Fr, True, True)

    Fz = x[k] | Equivalent(Z, z[k])
    Cz = to_cnf(Fz, True, True)

    Fu = Equivalent(u, x[k])
    Cu = to_cnf(Fu, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fz))
    print("# "+ str(Fu))
    print("def T2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    Z = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cz))

    print()   
    print("    u = cnf.add_var()")
    to_py(	   str(Cu))
    print("    cnf.add_weight(u, 0.707106781186548)")
    print()   

    print("    cnf.vars.z[k] = Z")
    print("    cnf.vars.r = R")
    print()

    #Tdag
    Fr = Equivalent(R, r ^ (x[k] & ~z[k] & Z))
    Cr = to_cnf(Fr, True, True)

    Fz = x[k] | Equivalent(Z, z[k])
    Cz = to_cnf(Fz, True, True)

    Fu = Equivalent(u, x[k])
    Cu = to_cnf(Fu, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fz))
    print("# "+ str(Fu))
    print("def Tdg2CNF(cnf, k):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    Z = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cz))

    print()   
    print("    u = cnf.add_var()")
    to_py(	   str(Cu))
    print("    cnf.add_weight(u, 0.707106781186548)")
    print()   

    print("    cnf.vars.z[k] = Z")
    print("    cnf.vars.r = R")
    print()

    #CNOT
    Fr = Equivalent(R, r ^ (x[c] & z[t] & (~x[t] ^ z[c])))
    Cr = to_cnf(Fr, True, True)

    Fx = Equivalent(X, x[t] ^ x[c])
    Cx = to_cnf(Fx, True, True)

    Fz = Equivalent(Z, z[t] ^ z[c])
    Cz = to_cnf(Fz, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fx))
    print("# "+ str(Fz))
    print("def CNOT2CNF(cnf, c, t):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    Z = cnf.add_var()")
    print("    X = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cx))
    print()
    to_py(	   str(Cz))
    print("    cnf.vars.x[t] = X")
    print("    cnf.vars.z[c] = Z")
    print("    cnf.vars.r = R")
    print()
    
    #CZ
    Z1    = symbols('Z1')
    Z2    = symbols('Z2')
    Fr = Equivalent(R, r ^ (x[t] & x[c] & (z[t] ^ z[c])))
    Cr = to_cnf(Fr, True, True)

    Fz1 = Equivalent(Z1, z[c] ^ x[t])
    Cz1 = to_cnf(Fz1, True, True)

    Fz2 = Equivalent(Z2, z[t] ^ x[c])
    Cz2 = to_cnf(Fz2, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fx))
    print("# "+ str(Fz))
    print("def CZ2CNF(cnf, c, t):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    Z1 = cnf.add_var()")
    print("    Z2 = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cz1))
    print()
    to_py(	   str(Cz2))
    print()
    print("    cnf.vars.z[c] = Z1")
    print("    cnf.vars.z[t] = Z2")
    print("    cnf.vars.r = R")
    print()

    #RZ
    Fr = Equivalent(R, r ^ (x[k] & z[k] & ~Z))
    Cr = to_cnf(Fr, True, True)

    Fz = x[k] | Equivalent(Z, z[k])
    Cz = to_cnf(Fz, True, True)

    Fu1 = Equivalent(u1, x[k] & (((z[k] & Z)) | (~z[k] & ~Z)))
    Cu1 = to_cnf(Fu1, True, True)

    Fu2 = Equivalent(u2, x[k] & (((~z[k] & Z)) | (z[k] & ~Z)))
    Cu2 = to_cnf(Fu2, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fz))
    print("# "+ str(Fu))
    print("def RZ2CNF(cnf, k, cos_theta, sin_theta):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    Z = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cz))

    print()   
    print("    u1 = cnf.add_var()")
    to_py(	   str(Cu1))
    print("    cnf.add_weight( u1, cos_theta)")
    print("    cnf.add_weight(-u1, 1)")
    print()   

    print("    u2 = cnf.add_var()")
    to_py(	   str(Cu2))
    print("    cnf.add_weight( u2, sin_theta)")
    print("    cnf.add_weight(-u2, 1)")

    print("    cnf.vars.z[k] = Z")
    print("    cnf.vars.r = R")
    print()

    #RX
    Fr = Equivalent(R, r ^ (z[k] & ~x[k] & X))
    Cr = to_cnf(Fr, True, True)

    Fx = z[k] | Equivalent(X, x[k])
    Cx = to_cnf(Fx, True, True)

    Fu1 = Equivalent(u1, z[k] & ((x[k] & X) | (~x[k] & ~X)))
    Cu1 = to_cnf(Fu1, True, True)

    Fu2 = Equivalent(u2, z[k] & ((~x[k] & X) | (x[k] & ~X)))
    Cu2 = to_cnf(Fu2, True, True)

    print("# "+ str(Fr))
    print("# "+ str(Fx))
    print("# "+ str(Fu))
    print("def RX2CNF(cnf, k, cos_theta, sin_theta):")
    print("    r = cnf.vars.r")
    print("    x = cnf.vars.x")
    print("    z = cnf.vars.z")
    print("    R = cnf.add_var()")
    print("    X = cnf.add_var()")
    to_py(	   str(Cr))
    print()
    to_py(	   str(Cx))

    print()   
    print("    u1 = cnf.add_var()")
    to_py(	   str(Cu1))
    print("    cnf.add_weight( u1, cos_theta)")
    print("    cnf.add_weight(-u1, 1)")
    print()   

    print("    u2 = cnf.add_var()")
    to_py(	   str(Cu2))
    print("    cnf.add_weight( u2, sin_theta)")
    print("    cnf.add_weight(-u2, 1)")

    print("    cnf.vars.x[k] = X")
    print("    cnf.vars.r = R")
    print()
  


if __name__ == "__main__":
    main()