# 
# A quantum circuit is composed by quantum and classical bits.
#

# here are the objects that we use to create a quantum circuit
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit

# we use a quantum register to keep our quantum bits.
qreg =  QuantumRegister(1) # in this example we will use a single quantum bit

# To get an information from a quantum bit, it must be measured. (More details will appear.)
#     The measurement result is stored classically.
#     Therefore, we also use a classical regiser with classical bits
creg = ClassicalRegister(1) # in this example we will use a single classical bit

# now we can define our quantum circuit
# it is composed by a quantum and a classical register
mycircuit = QuantumCircuit(qreg,creg)

# we apply operators on quantum bits
# operators are also called as gates
# we apply NOT operator represented as "x"
# operator is a part of the circuit, and we should specify the quantum bit as the parameter
mycircuit.x(qreg[0]) # (quantum) bits are enumerated starting from 0
# NOT operator or x-gate is applied to the first qubit of the quantum register

# let's run our codes until now, and then draw our circuit
print("Everything looks fine, let's continue ...")

# we use matplotlib_circuit_drawer
#     we shortly refer it as "drawer" in our codes
from qiskit.tools.visualization import matplotlib_circuit_drawer as drawer

# let's draw our circuit now 
drawer(mycircuit)
# re-execute me if you DO NOT see the circuit diagram

# measurement is defined by associating a quantum bit to a classical bit
mycircuit.measure(qreg[0],creg[0])
# the result will be stored in the classical bit

print("Everything looks fine, let's continue ...")

# let's draw the circuit again to see how the measurement is defined
drawer(mycircuit)
# reexecute me if you DO NOT see the circuit diagram

# we are done with design of our circuit
# now we can execute it
# we execute quantum circuits many times (WHY?)

# we use method "execute" and object "Aer" from qiskit library
from qiskit import execute, Aer

# we create a job object for execution of the circuit
# there are three parameters
#     1. mycircuit
#     2. beckend on which it will be executed: we will use local simulator
#     3. how_many_times will it be executed, let's pick it as 1024
job = execute(mycircuit,Aer.get_backend('qasm_simulator'),shots=1024)

# we can get the result of the outcome as follows
counts = job.result().get_counts(mycircuit)
print(counts)

# usually quantum programs produce probabilistic outcomes 

# 
# My second quantum circuit
#

# we import all at once
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer
from qiskit.tools.visualization import matplotlib_circuit_drawer as drawer

# we will use 4 quantum bits and 4 classical bits
qreg2 =  QuantumRegister(4)
creg2 = ClassicalRegister(4)

mycircuit2 = QuantumCircuit(qreg2,creg2)

# I will apply x-gate to the first quantum bit twice
mycircuit2.x(qreg2[0])
mycircuit2.x(qreg2[0])

# I will apply x-gate to the fourth quantum bit once
mycircuit2.x(qreg2[3])

# I will apply x-gate to the third quantum bit three times
mycircuit2.x(qreg2[2])
mycircuit2.x(qreg2[2])
mycircuit2.x(qreg2[2])

# I will apply x-gate to the second quantum bit four times
mycircuit2.x(qreg2[1])
mycircuit2.x(qreg2[1])
mycircuit2.x(qreg2[1])
mycircuit2.x(qreg2[1])

# if the size of quantum and classical registers are the same, we can define measurements with a single line code
mycircuit2.measure(qreg2,creg2)
# then each quantum bit and classical bit is associated with respect to their indices

# let's run our codes until now, and then draw our circuit

print("Everything looks fine, let's continue ...")

drawer(mycircuit2)
# re-execute me if you DO NOT see the circuit diagram

job = execute(mycircuit2,Aer.get_backend('qasm_simulator'),shots=500)
counts = job.result().get_counts(mycircuit2)
print(counts)

def print_outcomes(counts): # takes a dictionary variable
    for outcome in counts: # for each key-value in dictionary
        reverse_outcome = ''
        for i in outcome: # each string can be considered as a list of characters
            reverse_outcome = i + reverse_outcome # each new symbol comes before the old symbol(s)
        print(reverse_outcome,"is observed",counts[outcome],"times")

job = execute(mycircuit2,Aer.get_backend('qasm_simulator'),shots=1024)
counts = job.result().get_counts(mycircuit2) # counts is a dictionary object in python
print_outcomes(counts)


from random import randrange
n = 20
r=randrange(n) # pick a number from the list {0,1,...,n-1}
print(r)

# test this method by using a loop
for i in range(10):
    print(randrange(n))

#
# your solution is here
#
