# Import the Qiskit SDK
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import available_backends, execute, register, get_backend, compile
from qiskit.tools.visualization import plot_histogram, circuit_drawer
import time

pi = 3.14159265359

qx_config = {
        "APItoken": "4c79a44d415dcf5c0c967e7fb488a39c3c6042baacfd60861d6bca9326aeeb64022892979076e63e0bd6c6e007d4ef3d004b697a022bfaf469e8b0a999efdb63",
        "url":"https://quantumexperience.ng.bluemix.net/api"}

register(qx_config['APItoken'], qx_config['url'])
available_backends()

from qiskit import least_busy
least_busy(available_backends({'simulator': False}))

# couplingMap = [(0,1),(0,2),(1,2),(2,3),(4,2),(4,3)]
couplingMap = [(1,0),(2,0),(2,1),(3,2),(3,4),(4,2)]

def cRotation(qc, q, i, j, angle):
    
    half_a = angle / 2
    if i != 2 and j != 2:
        swap(qc, q, 2, j)
        cRotation(qc, q, i, 2, angle)
        swap(qc, q, 2, j)
    else:
        qc.u1(half_a, q[i])
        if(j,i) in couplingMap:
            qc.cx(q[j], q[i])
        else:
            invertCNOT(qc, q, i, j)
        qc.u1(-half_a, q[i])
        if(j,i) in couplingMap:
            qc.cx(q[j], q[i])
        else:
            invertCNOT(qc, q, i, j)
        qc.u1(half_a, q[j])
        
def invertCNOT(qc, q, i, j):
    qc.h(q[i])
    qc.h(q[j])
    qc.cx(q[i], q[j])
    qc.h(q[i])
    qc.h(q[j])
    
def swap(qc, q, i, j):
    if i != 2 and j != 2:
        swap(qc, q, 2, j)
        swap(qc, q, i, 2)
        swap(qc, q, 2, j)
    else:
        if (i,j) in couplingMap:
            qc.cx(q[i], q[j])  
            invertCNOT(qc, q, i, j)
            qc.cx(q[i], q[j])
        else:
            qc.cx(q[j], q[i])  
            invertCNOT(qc, q, j, i)
            qc.cx(q[j], q[i])
            
def inverse_qft(qc, q, indices):
        
    rev = list(reversed(indices))
    
    
    #swaps need to happen
    N = len(rev)
    
    for i in  range(int(N / 2)):
        j = N - i - 1
        swap(qc, q, i, j)
    
    for n, i in enumerate(rev):
        
        angle = -pi/2**n
        
        for j in rev[:n]:
            
            cRotation(qc, q, i, j, angle)
            angle *= 2
    
        qc.h(q[i])

# Create a Quantum Register with 5 qubits.
q = QuantumRegister(5)
# Create a Classical Register with 5 bits for storing results.
c = ClassicalRegister(5)
# Create a Quantum Circuit
qc = QuantumCircuit(q, c)

# Add a H gate on 4 qubits, putting the qubits in superposition.

qc.h(q[0])
qc.h(q[1])
qc.h(q[2])
qc.h(q[3])

#Put qubit to |1>
qc.x(q[4])

cRotation(qc, q, 3, 4, pi/8)

cRotation(qc, q, 2, 4, pi/4)

cRotation(qc, q, 1, 4, pi/2)

cRotation(qc, q, 0, 4, pi)

inverse_qft(qc, q, range(4))


qc.measure(q, c)

# See a list of available local simulators
print("Local backends: ", available_backends({'local': True}))

# Compile and run the Quantum circuit on a simulator backend
job = execute(qc, "local_qasm_simulator")

my_backend = get_backend('ibmqx4')
qobj = compile(qc, backend=my_backend, shots=1024)
job = my_backend.run(qobj)

lapse = 0
interval = 30
while not job.done:
    print('Status @ {} seconds'.format(interval * lapse))
    print(job.status)
    time.sleep(interval)
    lapse += 1
print(job.status)

result = job.result()
counts = result.get_counts()
print(result)
print(counts)

plot_histogram(counts)

circuit_drawer(qc)

print(qc.qasm())