# Importing standard Qiskit libraries and configuring account
from qiskit import QuantumRegister, ClassicalRegister, execute
from qiskit import QuantumCircuit, execute, Aer
from qiskit.tools.jupyter import *
import matplotlib.pyplot as plt
import numpy as np
from math import pi
%matplotlib inline

def real_map(value, leftMin, leftMax, rightMin, rightMax):
    # Maps one range to another
    # Figure out how 'wide' each range is
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin

    # Convert the left range into a 0-1 range (float)
    valueScaled = float(value - leftMin) / float(leftSpan)

    # Convert the 0-1 range into a value in the right range.
    return rightMin + (valueScaled * rightSpan)

# Quantum Random Number generator
def QRandom(a, b, qubits):
    q = QuantumRegister(qubits, 'q')
    circ = QuantumCircuit(q)
    c0 = ClassicalRegister(1, 'c0')
    circ.add_register(c0)

    for i in range(qubits):
        circ.h(q[i])

    for i in range(qubits):
        circ.measure(q[i], c0)

    backend = Aer.get_backend('statevector_simulator')
    job = execute(circ, backend)
    result = job.result()
    output = result.get_statevector(circ, decimals=5)

    n1 = 0
    n2 = 0
    n3 = 0
    for i in range( output.size ):
        if abs(output[i]) != 0:
            n1 = i
            n2 = np.real(output[i])
            n3 = np.imag(output[i])
    
    y = real_map(n1+n2+n3, -qubits, len(output)-1+qubits, a, b) 
    return y

y = []
for i in range(20):
    y.append(QRandom(0,100,3))
    
plt.plot(y)

# Example of number of unique random numbers:

# Generating 40 random numbers with our QRNG

y = []
for i in range(40):
    y.append(QRandom(0,100,3))

count = 0

z=[]

#Counting the number of unique elements

for num in y:
    if num not in z:
        count = count+1
        z.append(num)
print('The number of unique elements in provided random distribution is:')
print(count)

# Variational Quantum Random Number generator
def VarQRandom(a, b, theta, depth, qubits):
    
    q = QuantumRegister(qubits, 'q')
    circ = QuantumCircuit(q)
    c0 = ClassicalRegister(2, 'c0')
    circ.add_register(c0)
    
    for j in range(depth):
        
        for i in range(qubits):
            circ.h(q[i])
            #rotating the axis by an angle theta along y-axis
            circ.ry(theta, q[i])
            #rotating the axis by an angle theta along x-axis
            circ.rx(theta, q[i])

    for i in range(qubits):
        circ.measure(q[i], c0)

    backend = Aer.get_backend('statevector_simulator')
    job = execute(circ, backend)
    result = job.result()
    output = result.get_statevector(circ, decimals=5)

    n1 = 0
    n2 = 0
    n3 = 0
    for i in range( output.size ):
        if abs(output[i]) != 0:
            n1 = i
            n2 = np.real(output[i])
            n3 = np.imag(output[i])
    
    y = real_map(n1+n2+n3, -qubits, len(output)-1+qubits, a, b)
    return y

#initialization
m=0
length = 0
theta = 0

#taking input from the user
number = int(input('Enter the number of random numbers required'))
low = int(input('Enter lower limit'))
high = int(input('Enter higher limit'))

depth=1
x=[]
n=1
while length!=number:
    y = VarQRandom(low,high,theta,depth,3)
    while (y in x):
        i=i+1
        theta = theta + pi/16
        y= VarQRandom(low,high,theta,depth,3)
        if(i==(16*n)):
            depth = depth+1
            n=n+1
    x.append(y)
    length=len(x) 

plt.plot(x)

count = 0

z=[]

#Counting the number of unique elements

for num in x:
    if num not in z:
        count = count+1
        z.append(num)
print('The number of unique elements in provided random distribution is:')
print(count)

y2 = []
for i in range(20):
    y2.append(QRandom(0,100,2))
    
plt.plot(y2)

z2=[]
count = 0
#Counting the number of unique elements

for num in y2:
    if num not in z2:
        count = count+1
        z2.append(num)
print('The number of unique elements in provided random distribution is:')
print(count)

#initialization
m=0
length = 0
theta = 0

#taking input from the user
number = int(input('Enter the number of random numbers required'))
low = int(input('Enter lower limit'))
high = int(input('Enter higher limit'))

depth=1
x2=[]
n=1
while length!=number:
    y = VarQRandom(low,high,theta,depth,2)
    while (y in x2):
        i=i+1
        theta = theta + pi/16
        y= VarQRandom(low,high,theta,depth,2)
        if(i==(16*n)):
            depth = depth+1
            n=n+1
    x2.append(y)
    length=len(x2) 

plt.plot(x2)

z2=[]
count = 0
#Counting the number of unique elements

for num in x2:
    if num not in z2:
        count = count+1
        z2.append(num)
print('The number of unique elements in provided random distribution is:')
print(count)

from datetime import datetime

start_time = datetime.now()

y3 = []
for i in range(100):
    y3.append(QRandom(0,100,2))

end_time = datetime.now()

print('Duration: {}'.format(end_time - start_time))

#initialization
m=0
length = 0
theta = 0

#taking input from the user
number = int(input('Enter the number of random numbers required'))
low = int(input('Enter lower limit'))
high = int(input('Enter higher limit'))

depth=1
x2=[]
n=1
start_time = datetime.now()

while length!=number:
    y = VarQRandom(low,high,theta,depth,3)
    while (y in x2):
        i=i+1
        theta = theta + pi/16
        y= VarQRandom(low,high,theta,depth,3)
        if(i==(16*n)):
            depth = depth+1
            n=n+1
    x2.append(y)
    length=len(x2) 

end_time = datetime.now()

print('Duration: {}'.format(end_time - start_time))

#initialization
m=0
length = 0
theta = 0

#taking input from the user
number = int(input('Enter the number of random numbers required'))
low = int(input('Enter lower limit'))
high = int(input('Enter higher limit'))

depth=1
x4=[]
n=1

start_time = datetime.now()

while length!=number:
    y = VarQRandom(low,high,theta,depth,2)
    while (y in x2):
        i=i+1
        theta = theta + pi/16
        y= VarQRandom(low,high,theta,depth,2)
        if(i==(16*n)):
            depth = depth+1
            n=n+1
    x4.append(y)
    length=len(x4) 

end_time = datetime.now()

print('Duration: {}'.format(end_time - start_time))

import random
testpy=[]
for i in range(20):
    y=random.randint(1,100)
    if y not in testpy: 
        testpy.append(y)

plt.plot(testpy)

#initialization
m=0
length = 0
theta = 0

#taking input from the user
number = int(input('Enter the number of random numbers required'))
low = int(input('Enter lower limit'))
high = int(input('Enter higher limit'))

depth=1
testvc=[]
n=1
while length!=number:
    y = VarQRandom(low,high,theta,depth,2)
    while (y in testvc):
        i=i+1
        theta = theta + pi/16
        y= VarQRandom(low,high,theta,depth,2)
        if(i==(16*n)):
            depth = depth+1
            n=n+1
    testvc.append(y)
    length=len(testvc)

plt.plot(testvc) 