# %matplotlib inline
# Importing standard Qiskit libraries and configuring account
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer, IBMQ
from qiskit.compiler import transpile, assemble
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from datetime import datetime
# import math
# Loading your IBM Q account(s)
# IBMQ.load_account()

def get_results_of_last_test():
    for bit in range(len(n_reg)):
        qc.measure(n_reg[bit], class_reg[bit])

    simulate = execute(qc, backend=backend, shots=1024).result()
    return simulate.get_counts()

def verify_results(expected):
    time_stamp = str(datetime.now())
    if [expected] == list(results.keys()):
        print("PASSING - {}", test_timestamp)
    else:
        print("FAILING - {}", test_timestamp)
        print(f'Got - {results}')
    

n_reg = QuantumRegister(3, 'n')
m_reg = QuantumRegister(3, 'm')
scratch_a = QuantumRegister(3, 'sca')
scratch_b = QuantumRegister(3, 'scb')
scratch_y = QuantumRegister(3, 'accm')
class_reg = ClassicalRegister(3, 'y')
qc = QuantumCircuit(n_reg, m_reg, scratch_a, scratch_b, scratch_y, class_reg)

# Register n set to 5
qc.x(n_reg[0])
qc.x(n_reg[1])
# qc.x(n_reg[2])
# Register m set to
# qc.x(m_reg[0])
qc.x(m_reg[1])

# global n_reg
# global m_reg
# global scratch_a
# global class_reg
# global backend
# global qc
# global test_timestamp

# def before_tests():
n_reg = QuantumRegister(3, 'n')
m_reg = QuantumRegister(2, 'm')
scratch_reg = QuantumRegister(3, 'sca')
class_reg = ClassicalRegister(3, 'y')
qc = QuantumCircuit(n_reg, m_reg, scratch_reg, class_reg)

backend = Aer.get_backend('qasm_simulator')
test_timestamp = str(datetime.now())

from ArithmaticFunctions import *

def before_each_ripple_borrow_test():
    n_reg = QuantumRegister(3, 'n')
    m_reg = QuantumRegister(2, 'm')
    scratch_a = QuantumRegister(3, 'sca')
    qc = QuantumCircuit(n_reg, m_reg, scratch_a, class_reg)

    # Set control bits
    qc.x(m_reg[1])
    
    return {'n_reg': n_reg, 'm_reg': m_reg, 'scratch_a': scratch_a, 'class_reg': class_reg, 'qc': qc }

# global n_reg
# global m_reg
# global scratch_a
# global class_reg
# global backend
# global qc
# global test_timestamp

# def before_tests():
n_reg = QuantumRegister(3, 'n')
m_reg = QuantumRegister(2, 'm')
scratch_reg = QuantumRegister(3, 'sca')
class_reg = ClassicalRegister(3, 'y')
qc = QuantumCircuit(n_reg, m_reg, scratch_reg, class_reg)

backend = Aer.get_backend('qasm_simulator')
test_timestamp = str(datetime.now())

from ArithmaticFunctions import *

# global n_reg
# global m_reg
# global scratch_a
# global class_reg
# global backend
# global qc
# global test_timestamp

# def before_tests():
n_reg = QuantumRegister(3, 'n')
m_reg = QuantumRegister(2, 'm')
scratch_reg = QuantumRegister(3, 'sca')
class_reg = ClassicalRegister(3, 'y')
qc = QuantumCircuit(n_reg, m_reg, scratch_reg, class_reg)

backend = Aer.get_backend('qasm_simulator')
test_timestamp = str(datetime.now())

from ArithmaticFunctions import *

# 1 - 1 = 0

"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[0])
qc.barrier()
c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[1], scratch_reg=scratch_a, start_shifting_at=0)

results = get_results_of_last_test()

# qc.draw()

verify_results(expected='000')

# 2 - 1 = 1
"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[1])
# qc.barrier()
c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=0)

results = get_results_of_last_test()
# qc.draw()

verify_results(expected='001')

# 4 - 1 = 3
"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[2])

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=0)
results = get_results_of_last_test()

# qc.draw()

verify_results(expected='011')

# 6 - 1 = 5
"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[1])
qc.x(n_reg[2])
# qc.barrier()
c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=0)
results = get_results_of_last_test()

qc.draw()

verify_results(expected='101')

# 5 - 1 = 4
"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[0])
qc.x(n_reg[2])

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=0)
results = get_results_of_last_test()
# qc.draw()

verify_results(expected='100')

# 5 - 2 = 3
"""
PASSED July 29 10:58am Pacific Time
"""
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[0])
qc.x(n_reg[2])

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=1)
results = get_results_of_last_test()

# qc.draw()

verify_results(expected='011')

# 7 - 2 = 5
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[0])
qc.x(n_reg[1])
qc.x(n_reg[2])

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=1)
results = get_results_of_last_test()

# qc.draw()

verify_results(expected='101')

# NO-OP(7) = 7
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

qc.x(n_reg[0])
qc.x(n_reg[1])
qc.x(n_reg[2])
qc.barrier()
c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[False], scratch_reg=scratch_a, start_shifting_at=1)
results = get_results_of_last_test()

qc.draw()

verify_results(expected='111')

# 1 - 2 + 2 = ?
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']
qc.x(n_reg[0])

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=2)
# ripple_carry_bits(circuit=qc, acc_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=1)
qc.barrier()
undo_ripple_borrow(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=2)

results = get_results_of_last_test()
qc.draw()
# print(results)

verify_results('001')

# 0 - 2 + 2 = ?
test_components = before_each_ripple_borrow_test()
qc = test_components['qc']

c_ripple_subtract(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=2)
# ripple_carry_bits(circuit=qc, acc_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=1)
qc.barrier()
undo_ripple_borrow(circuit=qc, min_reg=n_reg, control_bit=m_reg[True], scratch_reg=scratch_a, start_shifting_at=2)

results = get_results_of_last_test()
qc.draw()
# print(results)

verify_results('000')

def sub_once_mod(circuit, )

# global n_reg
# global m_reg
# global scratch_a
# global class_reg
# global backend
# global qc
# global test_timestamp

# def before_tests():
n_reg = QuantumRegister(3, 'n')
m_reg = QuantumRegister(2, 'm')
scratch_reg = QuantumRegister(3, 'sca')
class_reg = ClassicalRegister(3, 'y')
qc = QuantumCircuit(n_reg, m_reg, scratch_reg, class_reg)

backend = Aer.get_backend('qasm_simulator')
test_timestamp = str(datetime.now())

from ArithmaticFunctions import *

def before_each_subtraction_test():
    n_reg = QuantumRegister(3, 'n')
    m_reg = QuantumRegister(3, 'm')
    scratch_reg = QuantumRegister(3, 'sca')
    qc = QuantumCircuit(n_reg, m_reg, scratch_reg, class_reg)
    
    return {'n_reg': n_reg, 'm_reg': m_reg, 'scratch_reg': scratch_reg, 'class_reg': class_reg, 'qc': qc }

test_components = before_each_subtraction_test()
qc = test_components['qc']

# Set n_reg to 00...1
qc.x(n_reg[0])
# m_reg is | 0 >

sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=test_components['n_reg'],
                      subtrhnd_reg=test_components['m_reg'],
                      scratch_reg=test_components['scratch_reg'])

results = get_results_of_last_test()
# qc.draw()
# print(results)

verify_results('001')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to 00...1
qc.x(comps['n_reg'][0])
# Set m_reg to 00...1
qc.x(comps['m_reg'][0])
qc.barrier()

sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
qc.draw()
# print(results)

verify_results('000')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...110
qc.x(comps['n_reg'][1])
qc.x(comps['n_reg'][2])
# Set m_reg to ...011
qc.x(comps['m_reg'][0])
qc.x(comps['m_reg'][1])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
qc.draw()
# print(results)

verify_results('011')

verify_results('011')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...10
qc.x(comps['n_reg'][1])
# Set m_reg to ...01
qc.x(comps['m_reg'][0])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
qc.draw()
# print(results)

verify_results('001')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...110
qc.x(comps['n_reg'][0])
qc.x(comps['n_reg'][1])
# Set m_reg to ...011
qc.x(comps['m_reg'][0])
qc.x(comps['m_reg'][1])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
# qc.draw()
# print(results)

verify_results('000')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...110
qc.x(comps['n_reg'][0])
qc.x(comps['n_reg'][1])
qc.x(comps['n_reg'][2])
# Set m_reg to ...011
qc.x(comps['m_reg'][1])
qc.x(comps['m_reg'][2])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
# qc.draw()
# print(results)

verify_results('001')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...110
qc.x(comps['n_reg'][1])
qc.x(comps['n_reg'][2])
# Set m_reg to ...011
qc.x(comps['m_reg'][1])
qc.x(comps['m_reg'][0])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
# qc.draw()
# print(results)

verify_results('011')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

# Set n_reg to ...110
qc.x(comps['n_reg'][2])
# Set m_reg to ...011
qc.x(comps['m_reg'][0])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()
# qc.draw()
# print(results)

verify_results('011')

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

qc.x(comps['n_reg'][0])
qc.x(comps['m_reg'][1])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

for bit in range(len(comps['scratch_reg'])):
    qc.measure(comps['scratch_reg'], comps['class_reg'])
    
results = execute(qc, backend=backend, shots=1024).result()

print(results.get_counts())

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

qc.x(comps['n_reg'][0])
qc.x(comps['m_reg'][1])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = get_results_of_last_test()

print(results)

# Get test components
comps = before_each_subtraction_test()
qc = comps['qc']

qc.x(comps['n_reg'][1])
qc.x(comps['m_reg'][1])
qc.x(comps['m_reg'][2])

qc.barrier()
sub_b_from_a_in_place(circuit=qc,
                      minnd_reg=comps['n_reg'],
                      subtrhnd_reg=comps['m_reg'],
                      scratch_reg=comps['scratch_reg'])

results = execute(qc, backend=backend, shots=1024).result()

print(results)

