# first lets make a table of the possible inputs and outputs, first we need to install tabulate using 
# pip the python package installer 
# make sure it has its own cell

pip install tabulate 

#Here are the possible inputs and corresponding outputs,
from tabulate import tabulate
print(tabulate([[ '|0>c |0>t', '|0>c |0>t' ], ['|0>c |1>t', '|0>c |1>t'], 
                [ '|1>c |0>t', '|1>c |1>t' ], 
                [ '|1>c |0>t', '|1>c |0>t' ]], headers=['Input', 'Output']))


from qiskit import * # this means that from the qiskit package
                     # import ALL functionality(*)
    
# we also want to see our results in insightful ways through graphs! 
from qiskit.visualization import * # bloch sphere graphs, bar graphs for measurements 

# We also need to import the simulators that we will use to make measurements

S_simulator=Aer.backends(name='statevector_simulator')[0] # allows you to simulate statevectors
M_simulator=Aer.backends(name='qasm_simulator')[0] # allows you to simulate measurements

# make a quantum circuit with two qubits and two classical bits 

qc= QuantumCircuit(2,2)

# Initialize first qubit to |1> so that we can see CNOT (cx) do something 
one = [0,1] 
qc.initialize(one, 0)


# print out initial state to confirm that something happens when we use our gate! 
initial_state=execute(qc, S_simulator).result().get_statevector()
print('Initial state: ', initial_state)

# use the Cx gate, first qubit as control and second qubit as target
qc.cx(0,1)

# print out results! 
state_after_gate= execute(qc, S_simulator).result().get_statevector()
print('state after CX gate: ', state_after_gate)

# example of DJ algorithm (CX for balanced)

# make circuit
dj = QuantumCircuit(2,2)

# Initialize second qubit
one=[0,1]
dj.initialize(one, 1)

# we use barries to seperate steps 
dj.barrier()

# Apply H gates 
dj.h(0)
dj.h(1)

dj.barrier()

# Use cx as Uf 
dj.cx(0,1)

dj.barrier()

dj.h(0)

dj.barrier()

dj.measure(0,0)

dj.draw(output='mpl')



plot_histogram(execute(dj, M_simulator).result().get_counts())

# example of DJ algorithm (CX for balanced)

# make circuit
dj = QuantumCircuit(2,2)

# Initialize second qubit
one=[0,1]
dj.initialize(one, 1)

# we use barries to seperate steps 
dj.barrier()

# Apply H gates 
dj.h(0)
dj.h(1)

dj.barrier()

# do nothing for constant function

dj.barrier()

dj.h(0)

dj.barrier()

dj.measure(0,0)

dj.draw(output='mpl')

plot_histogram(execute(dj, M_simulator).result().get_counts())