%matplotlib inline

# Importing standard Qiskit libraries
import random
from qiskit import execute, Aer, IBMQ
from qiskit.tools.jupyter import *
from qiskit.visualization import *

#Get the library to check the answers
%pip install -I git+https://github.com/mnp-club/MnP_QC_Workshop.git
from mnp_qc_workshop_2020.bb84 import *

# Configuring account
provider = IBMQ.load_account()
backend = provider.get_backend('ibmq_qasm_simulator')  # with this simulator it wouldn't work \

# Initial setup
random.seed(64) # do not change this seed, otherwise you will get a different key

# This is your 'random' bit string that determines your bases
numqubits = 16
bob_bases = str('{0:016b}'.format(random.getrandbits(numqubits)))

def bb84():
    print('Bob\'s bases:', bob_bases)

    # Now Alice will send her bits one by one...
    all_qubit_circuits = []
    for qubit_index in range(numqubits):

        # This is Alice creating the qubit
        thisqubit_circuit = alice_prepare_qubit(qubit_index)

        # This is Bob finishing the protocol below
        bob_measure_qubit(bob_bases, qubit_index, thisqubit_circuit)
        
        # We collect all these circuits and put them in an array
        all_qubit_circuits.append(thisqubit_circuit)
        
        
    # Now execute all the circuits for each qubit
    results = execute(all_qubit_circuits, backend=backend, shots=1).result()
    # And combine the results
    bits = ''
    for qubit_index in range(numqubits):
        bits += [measurement for measurement in results.get_counts(qubit_index)][0]
        
    return bits

# Here is your task
def bob_measure_qubit(bob_bases, qubit_index, qubit_circuit):
    if(bob_bases[qubit_index] == '1'):
        qubit_circuit.h(0)#Just change basis to +,- and then measure
    qubit_circuit.measure(0,0)
    
bits = bb84()      
print('Bob\'s bits: ', bits)
check_bits(bits)

alice_bases = '0100000101011100' # Alice's bases bits
key = ''
for i in range(16):
    if alice_bases[i]==bob_bases[i]:
        key = key + bits[i]#The key is only added to for the case where the bases are the same
print(key)
check_key(key)

message = get_message()# encrypted message
decrypted = ''
for i in range(344):
    if key[i%8] == '1':
        if message[i] == '0':
            m = '1'
        else:
            m = '0'
        decrypted = decrypted + m
    else:
        decrypted = decrypted + message[i]
#The key is sort of applied like a mask over each of the 43 8 bit sequences 
#of the 344 bitstring and its just a xor operation here
print(decrypted)
check_decrypted(decrypted)

decrypted_to_string_ASCII = ''

for i in range(43):
    decrypted_to_string_ASCII += str(chr(int(decrypted[(i*8):(i*8)+8],2)))#sequentially taking the 8 bit sequences
print(decrypted_to_string_ASCII)
check_message(decrypted_to_string_ASCII)