from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

q = QuantumRegister(2, "q")
c = ClassicalRegister(2, "c")

qc = QuantumCircuit(q,c)

qc.x(q[1])

qc.cx(q[1],q[0])

# Returning control qubit to the initial state
qc.x(q[1])

job = execute(qc,Aer.get_backend('unitary_simulator'), shots = 1)
U=job.result().get_unitary(qc,decimals=3)

print("CNOT(0) = ")
for row in U:
    s = ""
    for value in row:
        s = s + str(round(value.real,2)) + "  "
    print(s)

qc.draw(output="mpl", reverse_bits=True)

#
# your solution is here
#
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

q =  QuantumRegister(3,"q")
c = ClassicalRegister(3,"c")

qc = QuantumCircuit(q,c)

qc.x(q[2])
qc.x(q[1])
qc.ccx(q[2],q[1],q[0])
qc.x(q[2])
qc.x(q[1])

job = execute(qc,Aer.get_backend('unitary_simulator'), shots = 1)
U=job.result().get_unitary(qc,decimals=3)

print("CCNOT(00) = ")
for row in U:
    s = ""
    for value in row:
        s = s + str(round(value.real,2)) + "  "
    print(s)



qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

q =  QuantumRegister(3,"q")
c = ClassicalRegister(3,"c")

qc = QuantumCircuit(q,c)

qc.x(q[2])
qc.ccx(q[2],q[1],q[0])
qc.x(q[2])

job = execute(qc,Aer.get_backend('unitary_simulator'), shots = 1)
U=job.result().get_unitary(qc,decimals=3)

print("CCNOT(01) = ")
for row in U:
    s = ""
    for value in row:
        s = s + str(round(value.real,2)) + "  "
    print(s)



qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

q =  QuantumRegister(3,"q")
c = ClassicalRegister(3,"c")

qc = QuantumCircuit(q,c)

qc.x(q[1])
qc.ccx(q[2],q[1],q[0])
qc.x(q[1])

job = execute(qc,Aer.get_backend('unitary_simulator'), shots = 1)
U=job.result().get_unitary(qc,decimals=3)

print("CCNOT(10) = ")
for row in U:
    s = ""
    for value in row:
        s = s + str(round(value.real,2)) + "  "
    print(s)



qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

qaux = QuantumRegister(1,"qaux")
q = QuantumRegister(4,"q")
c = ClassicalRegister(4,"c")

qc = QuantumCircuit(q,qaux,c)

# step 1: set qaux to |1> if both q3 and q2 are in |1>
qc.ccx(q[3],q[2],qaux[0])

# step 2: apply NOT gate to q0 if both qaux and q1 are in |1>
qc.ccx(qaux[0],q[1],q[0])

# step 3: set qaux to |0> if both q3 and q2 are in |1> by reversing the affect of step 1
qc.ccx(q[3],q[2],qaux[0])

qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

all_inputs=[]
for q3 in ['0','1']:
    for q2 in ['0','1']:
        for q1 in ['0','1']:
            for q0 in ['0','1']:
                all_inputs.append(q3+q2+q1+q0)
# print(all_inputs)                                  

print("input --> output")
for the_input in all_inputs:
    # create the circuit
    qaux = QuantumRegister(1,"qaux")
    q = QuantumRegister(4,"q")
    c = ClassicalRegister(4,"c")
    qc = QuantumCircuit(q,qaux,c)
    # set the initial value of the circuit w.r.t. the input
    if the_input[0] =='1': qc.x(q[3])
    if the_input[1] =='1': qc.x(q[2])
    if the_input[2] =='1': qc.x(q[1])
    if the_input[3] =='1': qc.x(q[0])
    # implement the CCNOT gates
    qc.ccx(q[3],q[2],qaux[0])
    qc.ccx(qaux[0],q[1],q[0])
    qc.ccx(q[3],q[2],qaux[0])
    # measure the main quantum register 
    qc.measure(q,c)
    # execute the circuit
    job = execute(qc,Aer.get_backend('qasm_simulator'),shots=1)
    counts = job.result().get_counts(qc)
    for key in counts: the_output = key
    printed_str = the_input[0:3]+" "+the_input[3]+" --> "+the_output[0:3]+" "+the_output[3]
    if (the_input!=the_output): printed_str = printed_str + " the output is different than the input"
    print(printed_str)

#
# your solution is here
#
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

qaux = QuantumRegister(2,"qaux")
q = QuantumRegister(5,"q")
c = ClassicalRegister(5,"c")
qc = QuantumCircuit(q,qaux,c)

qc.ccx(q[4],q[3],qaux[1])
qc.ccx(q[2],q[1],qaux[0])
qc.ccx(qaux[1],qaux[0],q[0])
qc.ccx(q[4],q[3],qaux[1])
qc.ccx(q[2],q[1],qaux[0])

qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

all_inputs=[]
for q4 in ['0','1']: 
    for q3 in ['0','1']:
        for q2 in ['0','1']:
            for q1 in ['0','1']:
                for q0 in ['0','1']:
                    all_inputs.append(q4+q3+q2+q1+q0)
#print(all_inputs)  

print("input --> output")
for the_input in all_inputs:
    # create the circuit
    qaux = QuantumRegister(2,"qaux")
    q = QuantumRegister(5,"q")
    c = ClassicalRegister(5,"c")
    qc = QuantumCircuit(q,qaux,c)
    # set the initial value of the circuit w.r.t. the input
    if the_input[0] =='1': qc.x(q[4])
    if the_input[1] =='1': qc.x(q[3])
    if the_input[2] =='1': qc.x(q[2])
    if the_input[3] =='1': qc.x(q[1])
    if the_input[4] =='1': qc.x(q[0])
    # implement the CCNOT gates
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    qc.ccx(qaux[1],qaux[0],q[0])
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    # measure the main quantum register 
    qc.measure(q,c)
    # execute the circuit
    job = execute(qc,Aer.get_backend('qasm_simulator'),shots=1)
    counts = job.result().get_counts(qc)
    for key in counts: the_output = key
    printed_str = the_input[0:4]+" "+the_input[4]+" --> "+the_output[0:4]+" "+the_output[4]
    if (the_input!=the_output): printed_str = printed_str + " the output is different than the input"
    print(printed_str)

#
# your solution is here
#
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

qaux = QuantumRegister(2,"qaux")
q = QuantumRegister(5,"q")
c = ClassicalRegister(5,"c")
qc = QuantumCircuit(q,qaux,c)

qc.x(q[3])
qc.x(q[1])
qc.ccx(q[4],q[3],qaux[1])
qc.ccx(q[2],q[1],qaux[0])
qc.ccx(qaux[1],qaux[0],q[0])
qc.ccx(q[4],q[3],qaux[1])
qc.ccx(q[2],q[1],qaux[0])
qc.x(q[3])
qc.x(q[1])

qc.draw(output="mpl",reverse_bits=True)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

all_inputs=[]
for q4 in ['0','1']: 
    for q3 in ['0','1']:
        for q2 in ['0','1']:
            for q1 in ['0','1']:
                for q0 in ['0','1']:
                    all_inputs.append(q4+q3+q2+q1+q0)
#print(all_inputs)  

print("input --> output")
for the_input in all_inputs:
    # create the circuit
    qaux = QuantumRegister(2,"qaux")
    q = QuantumRegister(5,"q")
    c = ClassicalRegister(5,"c")
    qc = QuantumCircuit(q,qaux,c)
    # set the initial value of the circuit w.r.t. the input
    if the_input[0] =='1': qc.x(q[4])
    if the_input[1] =='1': qc.x(q[3])
    if the_input[2] =='1': qc.x(q[2])
    if the_input[3] =='1': qc.x(q[1])
    if the_input[4] =='1': qc.x(q[0])
    # implement the CCNOT gates
    qc.x(q[3])
    qc.x(q[1])
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    qc.ccx(qaux[1],qaux[0],q[0])
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    qc.x(q[3])
    qc.x(q[1])
    # measure the main quantum register 
    qc.measure(q,c)
    # execute the circuit
    job = execute(qc,Aer.get_backend('qasm_simulator'),shots=1)
    counts = job.result().get_counts(qc)
    for key in counts: the_output = key
    printed_str = the_input[0:4]+" "+the_input[4]+" --> "+the_output[0:4]+" "+the_output[4]
    if (the_input!=the_output): printed_str = printed_str + " the output is different than the input"
    print(printed_str)

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

all_inputs=[]
for q4 in ['0','1']: 
    for q3 in ['0','1']:
        for q2 in ['0','1']:
            for q1 in ['0','1']:
                for q0 in ['0','1']:
                    all_inputs.append(q4+q3+q2+q1+q0)
#print(all_inputs)  


def c4not(control_state='1111'):
    #
    # drawing the circuit
    #
    print("Control state is",control_state)
    print("Drawing the circuit:")
    qaux = QuantumRegister(2,"qaux")
    q = QuantumRegister(5,"q")
    c = ClassicalRegister(5,"c")
    qc = QuantumCircuit(q,qaux,c)

    for b in range(4):
        if control_state[b] == '0':
            qc.x(q[4-b])
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    qc.ccx(qaux[1],qaux[0],q[0])
    qc.ccx(q[4],q[3],qaux[1])
    qc.ccx(q[2],q[1],qaux[0])
    for b in range(4):
        if control_state[b] == '0':
            qc.x(q[4-b])
            
    display(qc.draw(output="mpl",reverse_bits=True))
    
    #
    # executing the operator on all possible inputs
    #
    print("Control state is",control_state)
    print("input --> output")
    for the_input in all_inputs:
        # create the circuit
        qaux = QuantumRegister(2,"qaux")
        q = QuantumRegister(5,"q")
        c = ClassicalRegister(5,"c")
        qc = QuantumCircuit(q,qaux,c)
        # set the initial value of the circuit w.r.t. the input
        if the_input[0] =='1': qc.x(q[4])
        if the_input[1] =='1': qc.x(q[3])
        if the_input[2] =='1': qc.x(q[2])
        if the_input[3] =='1': qc.x(q[1])
        if the_input[4] =='1': qc.x(q[0])
        # implement the CCNOT gates
        for b in range(4):
            if control_state[b] == '0':
                qc.x(q[4-b])
        qc.ccx(q[4],q[3],qaux[1])
        qc.ccx(q[2],q[1],qaux[0])
        qc.ccx(qaux[1],qaux[0],q[0])
        qc.ccx(q[4],q[3],qaux[1])
        qc.ccx(q[2],q[1],qaux[0])
        for b in range(4):
            if control_state[b] == '0':
                qc.x(q[4-b])
        # measure the main quantum register 
        qc.measure(q,c)
        # execute the circuit
        job = execute(qc,Aer.get_backend('qasm_simulator'),shots=1)
        counts = job.result().get_counts(qc)
        for key in counts: the_output = key
        printed_str = the_input[0:4]+" "+the_input[4]+" --> "+the_output[0:4]+" "+the_output[4]
        if (the_input!=the_output): printed_str = printed_str + " the output is different than the input"
        print(printed_str)

# try different values

#c4not()
#c4not('1001')
c4not('0011')
#c4not('1101')
#c4not('0000')