%run ../include/quantum.py

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

qreg = QuantumRegister(3)
#No need to define classical register as we are not measuring

mycircuit = QuantumCircuit(qreg)


#set ancilla
mycircuit.x(qreg[2])
mycircuit.h(qreg[2])

Uf(mycircuit,qreg)  
    
#set ancilla back
mycircuit.h(qreg[2])
mycircuit.x(qreg[2]) 


job = execute(mycircuit,Aer.get_backend('unitary_simulator'))
u=job.result().get_unitary(mycircuit,decimals=3)

#We are interested in the top-left 4x4 part
for i in range(4):
    s=""
    for j in range(4):
        val = str(u[i][j].real)
        while(len(val)<5): val  = " "+val
        s = s + val
    print(s)
    

mycircuit.draw(output='mpl')

def inversion(circuit,quantum_reg):
    
    
    #step 1
    circuit.h(quantum_reg[1])
    circuit.h(quantum_reg[0])
    
    #step 2
    circuit.x(quantum_reg[1])
    circuit.x(quantum_reg[0])

    #step 3
    circuit.ccx(quantum_reg[1],quantum_reg[0],quantum_reg[2])

    #step 4
    circuit.x(quantum_reg[1])
    circuit.x(quantum_reg[0])
    
    #step 5
    circuit.x(quantum_reg[2])
    
    #step 6
    circuit.h(quantum_reg[1])
    circuit.h(quantum_reg[0])

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

qreg1 =  QuantumRegister(3)

mycircuit1 = QuantumCircuit(qreg1)

#set ancilla qubit
mycircuit1.x(qreg1[2])
mycircuit1.h(qreg1[2])
    
inversion(mycircuit1,qreg1)

#set ancilla qubit back
mycircuit1.h(qreg1[2])
mycircuit1.x(qreg1[2])


job = execute(mycircuit1,Aer.get_backend('unitary_simulator'))
u=job.result().get_unitary(mycircuit1,decimals=3)
for i in range(4):
    s=""
    for j in range(4):
        val = str(u[i][j].real)
        while(len(val)<5): val  = " "+val
        s = s + val
    print(s)
    
mycircuit1.draw(output='mpl')

%run ..\include\quantum.py

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

qreg = QuantumRegister(3)
creg = ClassicalRegister(2)

mycircuit = QuantumCircuit(qreg,creg)

#Grover


#initial step - equal superposition
for i in range(2):
    mycircuit.h(qreg[i])

#set ancilla
mycircuit.x(qreg[2])
mycircuit.h(qreg[2])

mycircuit.barrier()

#change the number of iterations
iterations=1

#Grover's iterations.
for i in range(iterations):
    #query
    Uf(mycircuit,qreg)
    
    mycircuit.barrier()
    #inversion
    inversion(mycircuit,qreg)
    mycircuit.barrier()
    
    
#set ancilla back
mycircuit.h(qreg[2])
mycircuit.x(qreg[2]) 
    
mycircuit.measure(qreg[0],creg[0])
mycircuit.measure(qreg[1],creg[1])

job = execute(mycircuit,Aer.get_backend('qasm_simulator'),shots=10000)
counts = job.result().get_counts(mycircuit)

# print the outcome
for outcome in counts:
    print(outcome,"is observed",counts[outcome],"times")

mycircuit.draw(output='mpl')

def big_inversion(circuit,quantum_reg):

    for i in range(3):
        circuit.h(quantum_reg[i])
        circuit.x(quantum_reg[i])

    circuit.ccx(quantum_reg[1],quantum_reg[0],quantum_reg[4])
    circuit.ccx(quantum_reg[2],quantum_reg[4],quantum_reg[3])
    circuit.ccx(quantum_reg[1],quantum_reg[0],quantum_reg[4])

    for i in range(3):
        circuit.x(quantum_reg[i])
        circuit.h(quantum_reg[i])
    
    circuit.x(quantum_reg[3])

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

big_qreg2 =  QuantumRegister(5)

big_mycircuit2 = QuantumCircuit(big_qreg2)

#set ancilla
big_mycircuit2.x(big_qreg2[3])
big_mycircuit2.h(big_qreg2[3])
    
big_inversion(big_mycircuit2,big_qreg2)

#set ancilla back
big_mycircuit2.h(big_qreg2[3])
big_mycircuit2.x(big_qreg2[3])

job = execute(big_mycircuit2,Aer.get_backend('unitary_simulator'))
u=job.result().get_unitary(big_mycircuit2,decimals=3)
for i in range(8):
    s=""
    for j in range(8):
        val = str(u[i][j].real)
        while(len(val)<6): val  = " "+val
        s = s + val
    print(s)

%run ..\include\quantum.py

def big_inversion(circuit,quantum_reg):

    for i in range(3):
        circuit.h(quantum_reg[i])
        circuit.x(quantum_reg[i])

    circuit.ccx(quantum_reg[1],quantum_reg[0],quantum_reg[4])
    circuit.ccx(quantum_reg[2],quantum_reg[4],quantum_reg[3])
    circuit.ccx(quantum_reg[1],quantum_reg[0],quantum_reg[4])

    for i in range(3):
        circuit.x(quantum_reg[i])
        circuit.h(quantum_reg[i])
    
    circuit.x(quantum_reg[3])

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

qreg8 =  QuantumRegister(5)
creg8 = ClassicalRegister(3)

mycircuit8 = QuantumCircuit(qreg8,creg8)

#set ancilla
mycircuit8.x(qreg8[3])
mycircuit8.h(qreg8[3])

#Grover
for i in range(3):
    mycircuit8.h(qreg8[i])

mycircuit8.barrier()

#Try 1,2,6,12 8iterations of Grover
for i in range(2):
    Uf_8(mycircuit8,qreg8)
    mycircuit8.barrier()
    big_inversion(mycircuit8,qreg8)
    mycircuit8.barrier()

#set ancilla back

mycircuit8.h(qreg8[3])
mycircuit8.x(qreg8[3])

for i in range(3):
    mycircuit8.measure(qreg8[i],creg8[i])

job = execute(mycircuit8,Aer.get_backend('qasm_simulator'),shots=10000)
counts8 = job.result().get_counts(mycircuit8)
# print the reverse of the outcome
for outcome in counts8:
    print(outcome,"is observed",counts8[outcome],"times")

mycircuit8.draw(output='mpl')

%run ../include/quantum.py
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute, Aer

qreg12 =  QuantumRegister(19)
creg12 = ClassicalRegister(10)

mycircuit12 = QuantumCircuit(qreg12,creg12)

for i in range(10):
    mycircuit12.h(qreg12[i])

mycircuit12.x(qreg12[10])
mycircuit12.h(qreg12[10])

#number of iterations - change this value
iteration_count = 1
for i in range(iteration_count):
    giant_oracle2(mycircuit12,qreg12)
    giant_diffusion(mycircuit12,qreg12)
    
mycircuit12.h(qreg12[10])
mycircuit12.x(qreg12[10])

for i in range(10):
    mycircuit12.measure(qreg12[i],creg12[i])

job = execute(mycircuit12,Aer.get_backend('qasm_simulator'),shots=100000)
counts12 = job.result().get_counts(mycircuit12)
# print the reverse of the outcome
for outcome in counts12:
    print(outcome,"is observed",counts12[outcome],"times")

def oracle_11(circuit,qreg):
    circuit1.ccx(qreg[0],qreg[1],qreg[2])
                 

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

qreg1 =  QuantumRegister(3)

circuit1 = QuantumCircuit(qreg1)

# prepare ancilla qubit
circuit1.x(qreg1[2])
circuit1.h(qreg1[2])

#call the oracle
oracle_11(circuit1,qreg1)

# put ancilla qubit back into state |0>
circuit1.h(qreg1[2])
circuit1.x(qreg1[2])

job = execute(circuit1,Aer.get_backend('unitary_simulator'))
u=job.result().get_unitary(circuit1,decimals=3)
for i in range(4):
    s=""
    for j in range(4):
        val = str(u[i][j].real)
        while(len(val)<5): val  = " "+val
        s = s + val
    print(s)

circuit1.draw(output='mpl')

def oracle_01(circuit,qreg):
    circuit.x(qreg[1])
    circuit.ccx(qreg[0],qreg[1],qreg[2])
    circuit.x(qreg[1])

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

qreg1 =  QuantumRegister(3)

circuit1 = QuantumCircuit(qreg1)

# prepare ancilla qubit
circuit1.x(qreg1[2])
circuit1.h(qreg1[2])

#call the oracle
oracle_01(circuit1,qreg1)

# put ancilla qubit back into state |0>
circuit1.h(qreg1[2])
circuit1.x(qreg1[2])

job = execute(circuit1,Aer.get_backend('unitary_simulator'))
u=job.result().get_unitary(circuit1,decimals=3)
for i in range(4):
    s=""
    for j in range(4):
        val = str(u[i][j].real)
        while(len(val)<5): val  = " "+val
        s = s + val
    print(s)

circuit1.draw(output='mpl')

def oracle_00(circuit,qreg):
    circuit.x(qreg[0])
    circuit.x(qreg[1])
    circuit.ccx(qreg[0],qreg[1],qreg[2])
    circuit.x(qreg[0])
    circuit.x(qreg[1])

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

qreg = QuantumRegister(3)
creg = ClassicalRegister(2)

mycircuit = QuantumCircuit(qreg,creg)

#Grover


#initial step - equal superposition
for i in range(2):
    mycircuit.h(qreg[i])

#set ancilla
mycircuit.x(qreg[2])
mycircuit.h(qreg[2])

mycircuit.barrier()

#change the number of iterations
iterations=1

#Grover's iterations.
for i in range(iterations):
    #query
    oracle_00(mycircuit,qreg)
    
    mycircuit.barrier()
    #inversion
    inversion(mycircuit,qreg)
    mycircuit.barrier()
    
    
#set ancilla back
mycircuit.h(qreg[2])
mycircuit.x(qreg[2]) 
    
mycircuit.measure(qreg[0],creg[0])
mycircuit.measure(qreg[1],creg[1])

job = execute(mycircuit,Aer.get_backend('qasm_simulator'),shots=10000)
counts = job.result().get_counts(mycircuit)

# print the reverse of the outcome
for outcome in counts:
    reverse_outcome = ''
    for i in outcome:
        reverse_outcome = i + reverse_outcome
    print(reverse_outcome,"is observed",counts[outcome],"times")

mycircuit.draw(output='mpl')

def oracle_001_111(circuit,qreg):
    #Your code here
    #
    #