# Library for implementing XOR
import qiskit
import numpy as np
from qiskit import(
    IBMQ, QuantumCircuit, ClassicalRegister, QuantumRegister, execute,
    QuantumCircuit,
    execute,
    Aer)
from qiskit.visualization import plot_histogram, plot_state_city

if __name__ == "__main__":
    pass

def binary_it(alpha):
    ''' binary_it:
            returns a list that is indexed according to power,
                binaried[0] = is LSB.
    '''
    binaried = [int(x) for x in bin(alpha)[2:]]
    binaried.reverse()
    return binaried

def invert(alpha):
    return [ 1 - x for x in alpha]

def xor_gate_v_each(circuit,   a,b,r,   v,c,a0):
    ''' xor_gate_v_each:
            See paper 'An Example of the Difference Between Quantum and Classical Random Walks'
        
            args:
                circuit -- the py object that holds the quantum circuit.
                
                a -- q. reg. for the vertex.
                b -- q. reg. for holding neighbor vertex.
                r -- q. reg. for checking valid edge.
                
                f(c, a0) -- function that returns the neighboring
                         -- vertex of a0 using label c for the vertex label.
                c        -- label of the edge
                a0       -- label of the vertex
    '''
    
    #vc = v(c,a0);
    vc = 1
    
    #build in the not's for the a0 check.
    binaried = binary_it(a0)
        
    #match binaried
    if len(binaried) > len(a):
        binaried = binaried[:len(a)]
    elif len(a) > len(binaried):
        for i in range(len(a)-len(binaried)):
            binaried.append(0)
    
    notMask = invert(binaried)
    
    for i in range(len(notMask)):
        if notMask[i]:
            circuit.x(a[i]);
    
    
    #build in the cnots for xoring b
    binaried = binary_it(vc)
        
    #match binaried
    if len(binaried) > len(a):
        binaried = binaried[:len(a)]
    elif len(a) > len(binaried):
        for i in range(len(a)-len(binaried)):
            binaried.append(0)
    
    for i in range(len(binaried)):
        if binaried[i]:
            circuit.mct(a,b[i], None, mode='noancilla')
        
    #build in the not's for the a0 return.
    for i in range(len(notMask)):
        if notMask[i]:
            circuit.x(a[i]);

def xor_gate_f_each(circuit,   a,b,r,   v,c,a0):
    ''' xor_gate_v_each:
            See paper 'An Example of the Difference Between Quantum and Classical Random Walks'
        
            args:
                circuit -- the py object that holds the quantum circuit.
                
                a -- q. reg. for the vertex.
                b -- q. reg. for holding neighbor vertex.
                r -- q. reg. for checking valid edge.
                
                f(c, a0) -- function that returns the neighboring
                         -- vertex of a0 using label c for the vertex label.
                c        -- label of the edge
                a0       -- label of the vertex
    '''
    
    #fc = f(c,a0);
    fc = 1
    
    #build in the not's for the a0 check.
    binaried = binary_it(a0)
        
    #match binaried
    if len(binaried) > len(a):
        binaried = binaried[:len(a)]
    elif len(a) > len(binaried):
        for i in range(len(a)-len(binaried)):
            binaried.append(0)
    
    notMask = invert(binaried)
    
    for i in range(len(notMask)):
        if notMask[i]:
            circuit.x(a[i]);
    
    
    #build in the cnots for xoring b
    binaried = binary_it(fc)
        
    #match binaried
    if len(binaried) > len(r):
        binaried = binaried[:len(r)]
    elif len(r) > len(binaried):
        for i in range(len(r)-len(binaried)):
            binaried.append(0)
    
    for i in range(len(binaried)):
        if binaried[i]:
            circuit.mct(a,r[i], None, mode='noancilla')
        
    #build in the not's for the a0 return.
    for i in range(len(notMask)):
        if notMask[i]:
            circuit.x(a[i]);

n = 2

simulator = Aer.get_backend('qasm_simulator')

a = QuantumRegister(n, 'a')
b = QuantumRegister(n, 'b')
r = QuantumRegister(1, 'r')

circuit = QuantumCircuit(a, b, r)

a0 = 1
xor_gate_v_each(circuit, a,b,r, 0,0,a0)
circuit.barrier()
a0 = 2
xor_gate_f_each(circuit, a,b,r, 0,0,a0)
circuit.draw()

