from qiskit import QuantumCircuit, assemble, Aer
from qiskit.visualization import plot_histogram

from qiskit_textbook.widgets import binary_widget
binary_widget(nbits=6)

n = 8
n_q = n #number of qubits in the circuit.
n_b = n #number of output bits we will extract from the circuit at the end.
qc_output = QuantumCircuit(n_q,n_b)

#adding a measure operation to each of our eight qubits
#.measure(j,j): adds a measurement to our circuit that tells qubit j to write an output to bit j
for j in range(n):
    qc_output.measure(j,j)

#Visualing the circuit
qc_output.draw()

sim = Aer.get_backend('aer_simulator')  # this is the simulator we'll use
qobj = assemble(qc_output)  # this turns the circuit into an object our backend can run
result = sim.run(qobj).result()  # we run the experiment and get the result from that experiment
# from the results, we get a dictionary containing the number of times (counts)
# each result appeared
counts = result.get_counts()
# and display it on a histogram
plot_histogram(counts)

#NOT gate - i.e., X quantum gate
qc_encode = QuantumCircuit(n) #creating a quantum circuit of n qbits
qc_encode.x(7) #applying x gate into the 7th qubit
qc_encode.draw() #drawing the circuit

#obs: remember here that we already setted n = 8.

#creating a new circuit that contains the n qbits + the measuremnt part  
qc = qc_encode + qc_output
qc.draw()

#running and ploting the results
qobj = assemble(qc)
counts = sim.run(qobj).result().get_counts()
plot_histogram(counts)

#### Exercice: trying a new number: 36 =  100100
qc_encode2 = QuantumCircuit(6)
qc_encode2.x(2)
qc_encode2.x(5)

qc_encode2.draw()

n_q = 6 #number of qubits in the circuit.
n_b = 6 #number of output bits we will extract from the circuit at the end.
qc_output2 = QuantumCircuit(n_q,n_b)

for j in range(6):
    qc_output2.measure(j,j)

qc_output2.draw()   

qc2 = qc_encode2 + qc_output2
qc2.draw()

qobj2 = assemble(qc_output2)  # this turns the circuit into an object our backend can run
result2 = sim.run(qobj2).result()  # we run the experiment and get the result from that experiment
# from the results, we get a dictionary containing the number of times (counts)
# each result appeared
counts2 = result2.get_counts()
# and display it on a histogram
plot_histogram(counts2)

qobj2 = assemble(qc2)
counts2 = sim.run(qobj2).result().get_counts()
plot_histogram(counts2)

qc_cnot = QuantumCircuit(2) #quantum circuit of 2 qubits
qc_cnot.cx(0,1) #cnot between qubits 0 and 1
qc_cnot.draw() #draw the circuit

qc = QuantumCircuit(2,2)
qc.x(0) #apply x on qubit 0; It becomes 1
qc.cx(0,1) #apply cnot between qbit 0 and 1
qc.measure(0,0) 
qc.measure(1,1)
qc.draw()

qobja = assemble(qc)  # this turns the circuit into an object our backend can run
resulta = sim.run(qobja).result()  # we run the experiment and get the result from that experiment
# from the results, we get a dictionary containing the number of times (counts)
# each result appeared
countsa = resulta.get_counts()
# and display it on a histogram
plot_histogram(countsa)

qc_ha = QuantumCircuit(4,2)
# encode inputs in qubits 0 and 1
qc_ha.x(0) # For a=0, remove the this line. For a=1, leave it.
qc_ha.x(1) # For b=0, remove the this line. For b=1, leave it.
qc_ha.barrier()
# use cnots to write the XOR of the inputs on qubit 2
qc_ha.cx(0,2)
qc_ha.cx(1,2)
# use ccx to write the AND of the inputs on qubit 3
qc_ha.ccx(0,1,3)
qc_ha.barrier()
# extract outputs
qc_ha.measure(2,0) # extract XOR value
qc_ha.measure(3,1) # extract AND value

qc_ha.draw()

qobj = assemble(qc_ha)
counts = sim.run(qobj).result().get_counts()
plot_histogram(counts)