from qiskit import *
from qiskit.visualization import plot_histogram
%config InlineBackend.figure_format = 'svg'

qc_ab = QuantumCircuit(6,6) #Create a quantum circuit with 6 qubits and 6 classical bits


##ENCODE BIT STRING
#The random bit sequence Alice needs to encode is: 100100, so the first and fourth qubits are flipped from |0> -> |1>
qc_ab.x(0) #The first qubit is indexed at 0, following Python being zero-indexed. From now on it'll be referred to as qubit 0 and so on.
qc_ab.x(3) 
qc_ab.barrier()


##ALICE CHOOSES
#Alice randomly chooses to apply an X or an H.  
#Note that since the state is already either a |0> or |1>, a Z essentially leaves the qubit state unchanged. But let's write it anyway, shall we? 

qc_ab.h(0) # or qc.z(0) # switch these based on your own choice
qc_ab.z(1) # or qc.h(1)
qc_ab.z(2) # or qc.h(2)
qc_ab.h(3) # or qc.z(3)
qc_ab.z(4) # or qc.h(4)
qc_ab.h(5) # or qc.z(5)
qc_ab.barrier()


##BOB CHOOSES
#Alice sends the qubit sequence to Bob, and Bob randomly chooses measurements
qc_ab.h(0) # or qc.z(0) # switch these based on your own choice
qc_ab.z(1) # or qc.h(1)
qc_ab.h(2) # or qc.z(2)
qc_ab.h(3) # or qc.z(3)
qc_ab.z(4) # or qc.h(4)
qc_ab.z(5) # or qc.h(5)
qc_ab.barrier()


##PUBLICIZE CHOICES
#Alice and Bob publicize their choices and only retain those for which their choices match. In this case: qubits 0,1,3,4. 
#Note: technically Bob performs the measurement BEFORE publicizing, but we're combining the two here since no one is actually communicating.

qc_ab.measure(0,0)
qc_ab.measure(1,1)
qc_ab.measure(3,3)
qc_ab.measure(4,4)


#qc_ab.measure(2,2) #come back to uncomment these to see what happens to the results after you've run this once
#qc_ab.measure(5,5)


qc_ab.draw(output='mpl') #let's see what this circuit looks like!





##EXECUTE
result = execute(qc_ab, Aer.get_backend('qasm_simulator')).result().get_counts() #We're only making use of the simulator. Refer to [2] to see how you can run this on a real quantum computer.
plot_histogram(result)

#Same situation but now with sneaky Eve 

qc_aeb = QuantumCircuit(6,6) #Create a quantum circuit with 6 qubits and 6 classical bits

##ENCODE BIT STRING
qc_aeb.x(0) 
qc_aeb.x(3) 
qc_aeb.barrier()

##ALICE CHOOSES
qc_aeb.h(0) 
qc_aeb.z(1) 
qc_aeb.z(2) 
qc_aeb.h(3) 
qc_aeb.z(4) 
qc_aeb.h(5) 
qc_aeb.barrier()

##EVE CHOOSES
qc_aeb.h(0) #play around with these to see how many states with non-zero probabilities show up at the end for a fixed set of Alice's and Bob's choices
qc_aeb.z(1) 
qc_aeb.h(2) 
qc_aeb.h(3) 
qc_aeb.z(4) 
qc_aeb.z(5) 
qc_aeb.barrier()

##BOB CHOOSES
qc_aeb.h(0) 
qc_aeb.z(1) 
qc_aeb.h(2) 
qc_aeb.h(3) 
qc_aeb.z(4) 
qc_aeb.z(5) 
qc_aeb.barrier()


##PUBLICIZE CHOICES

qc_aeb.measure(0,0)
qc_aeb.measure(1,1)
qc_aeb.measure(3,3)
qc_aeb.measure(4,4)

#qc_aeb.measure(2,2) #come back to uncomment these to see what happens to the results after you've run this once
#qc_aeb.measure(5,5)

qc_aeb.draw(output='mpl') #let's see what this circuit looks like!

##EXECUTE
result = execute(qc_aeb, Aer.get_backend('qasm_simulator')).result().get_counts()
plot_histogram(result)

