import numpy as np
import pylab as plt

from qiskit import QuantumCircuit, execute, Aer, IBMQ
from qiskit.visualization import plot_histogram, plot_bloch_multivector


qpe = QuantumCircuit(4,3)
qpe.x(3)
qpe.draw('mpl')

#Apply Hadamard gate to all the quits
for q in range(3):
    qpe.h(q)
qpe.draw('mpl')

repetitions = 1
for q in range(3):
    for i in range(repetitions):
        qpe.cu1(np.pi/4., q, 3) # controlled U gate : for T gate angle = pi/4
    repetitions *= 2
qpe.draw('mpl')

from qiskit.circuit.library import QFT

def qft_dagger(qc, n):
    """n-qubit QFTdagger the first n qubits in the circuit qc"""
    for q in range(n//2):
        qc.swap(q, n-q-1)
    for j in range(n):
        for m in range(j):
            qc.cu1(-np.pi/(float(2**(j-m))), m, j)
        qc.h(j)

def qft_dagger2(qc, n):
    """n-qubit QFTdagger the first n qubits in the circuit qc"""
    
    ### WRITE YOUR CODE BETWEEN THESE LINES - START
    gate = QFT(n, inverse=True, do_swaps=True )
    qc.append(gate, range(n))
    
    ### WRITE YOUR CODE BETWEEN THESE LINES - END


# measure the counting register

qpe.barrier()
qft_dagger(qpe, 3)
qpe.barrier()
for n in range(3):
    qpe.measure(n,n)

qpe.draw('mpl')

backend = Aer.get_backend('qasm_simulator')
shots = 2048
results = execute(qpe, backend=backend, shots=shots).result()
answer = results.get_counts()

plot_histogram(answer)

def bin2decimal(binary_str):
    if '1' not in binary_str:
        return 0
    else:        
        binary = int(binary_str.lstrip('0'))

        binary1 = binary
        decimal, i = 0, 0
        while(binary != 0): 
            dec = binary % 10
            decimal = decimal + dec * pow(2, i) 
            binary = binary//10
            i += 1
        return decimal

bin2decimal('001010')

def QPE(N=3):
    qc = QuantumCircuit(N+1, N)
    qc.x(N)
        
    #Apply Hadamard gate to all the quits
    for q in range(N):
        qc.h(q)
    
    #Next we perform the controlled unitary operations
    repetitions = 1
    for q in range(N):
        for i in range(repetitions):
            qc.cu1(np.pi/4., q, N) # controlled U gate, angle is pi/4 for T gate
        repetitions *=2
       
    # measure the counting register
    qc.barrier()
    qft_dagger(qc, N)
    qc.barrier()
    for n in range(N):
        qc.measure(n,n)

    # execute the circuit
    backend = Aer.get_backend('qasm_simulator')
    Nshots = 2048
    results = execute(qc, backend=backend, shots=Nshots).result()
    counts = results.get_counts()
    
    for binary in counts:
        dec = bin2decimal(binary)
        phase = dec/(2**N)
        print ('binary:', binary,', decimal:', dec, 'Count ratio:', counts[binary]/Nshots)
        print ('phase:', phase)

    return counts
    #print (counts)

counts = QPE(4)
print (counts)

plot_histogram(counts)


def getting_more_precision(N=3):
    qc = QuantumCircuit(N+1, N)
        
    # Prepare our eigenstate |psi>:
    qc.x(N)
    
    #Apply Hadamard gate to all the quits
    for q in range(N):
        qc.h(q)
    
    #controlled-U operations
    angle = 2*np.pi/3.
    
    repetitions = 1
    for q in range(N):
        for i in range(repetitions):
            qc.cu1(angle, q, N) # controlled U gate
        repetitions *=2
       
    # measure the counting register
    qc.barrier()
    qft_dagger(qc, N)
    qc.barrier()
    for n in range(N):
        qc.measure(n,n)

    # execute the circuit
    backend = Aer.get_backend('qasm_simulator')
    Nshots = 2048
    results = execute(qc, backend=backend, shots=Nshots).result()
    counts = results.get_counts()

    return counts

def calcPhase(counts, Nqubit):
    
    kL = list(counts.items())
    vL = list(counts.values())
    
    maxval1 = max(vL); (k1,v1) = kL[vL.index(maxval1)]; vL.remove(maxval1)
    maxval2 = max(vL); (k2,v2) = kL[vL.index(maxval2)];
    
    k1_dec = bin2decimal(k1)
    k2_dec = bin2decimal(k2)
    
    phase1 = k1_dec/(2**Nqubit)
    phase2 = k2_dec/(2**Nqubit)
    
    print ('the phase lies between [',phase1, ',', phase2, ']')

N=5
counts = getting_more_precision(N)
print (counts)
calcPhase(counts, N)

plot_histogram(counts)


#'11010': 1385
26/32
print ( 6/8, 4/8)

d = {'001': 60, '011': 1397, '010': 358}

list(d.keys())[0]

