from qiskit import QuantumCircuit, QuantumRegister, Aer, execute, ClassicalRegister
from qiskit.ignis.verification.topological_codes import RepetitionCode
from qiskit.ignis.verification.topological_codes import lookuptable_decoding
from qiskit.ignis.verification.topological_codes import GraphDecoder
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors import pauli_error, depolarizing_error
from qiskit.ignis.mitigation.measurement import (complete_meas_cal,CompleteMeasFitter)
from qiskit.visualization import plot_histogram
import random
backend = Aer.get_backend('qasm_simulator')
#These two functions were taken from https://stackoverflow.com/questions/10237926/convert-string-to-list-of-bits-and-viceversa

def tobits(s):
    result = []
    for c in s:
        bits = bin(ord(c))[2:]
        bits = '00000000'[len(bits):] + bits
        result.extend([int(b) for b in bits])
    return ''.join([str(x) for x in result])

def frombits(bits):
    chars = []
    for b in range(int(len(bits) / 8)):
        byte = bits[b*8:(b+1)*8]
        chars.append(chr(int(''.join([str(bit) for bit in byte]), 2)))
    return ''.join(chars)

def get_noise(circuit,probability,qubits):
    random_number = random.uniform(0, 1)
    if(random_number <= probability):
        qubit = random.randint(0,len(qubits)-1)
        if(random.randint(0,3) == 0):
            circuit.x(qubit)
        if(random.randint(0,3) == 1):
            circuit.y(qubit)
        if(random.randint(0,3) == 2):
            circuit.z(qubit)
    return circuit

def codificate(bitString):
    qubits = list()
    for i in range(len(bitString)):
        mycircuit = QuantumCircuit(1,1)
        if(bitString[i] == "1"):
            mycircuit.x(0)
        mycircuit.h(0)
        qubits.append(mycircuit)
    return qubits

m0 = tobits("I like dogs") 
qubits = codificate(m0)
measurements = list()
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.h(0)
    qubit.measure(0,0)
    result = execute(qubit, backend, shots=1, memory=True).result()
    measurements.append(int(result.get_memory()[0]))
print(frombits(measurements))

for i in range(len(qubits)):
    cb = QuantumRegister(1,'code_qubit')
    lq = QuantumRegister(8,'ancilla_qubit')
    out = ClassicalRegister(1,'output_bit')
    mycircuit = QuantumCircuit(cb,lq,out)
    if(m0[i] == "1"):
        mycircuit.x(0)
    mycircuit.cx(0,[3,6])
    mycircuit.h([0,3,6])
    mycircuit.cx(0,[1,2])
    mycircuit.cx(3,[4,5])
    mycircuit.cx(6,[7,8])
    mycircuit.barrier()
    mycircuit = get_noise(mycircuit,0.2,range(9))
    mycircuit.barrier()
    mycircuit.cx(0,[1,2])
    mycircuit.cx(3,[4,5])
    mycircuit.cx(6,[7,8])
    mycircuit.ccx(2,1,0)
    mycircuit.ccx(5,4,3)
    mycircuit.ccx(8,7,6)
    mycircuit.h([0,3,6])
    mycircuit.cx(0,[3,6])
    mycircuit.ccx(6,2,0)
    mycircuit.measure(0,0)
    qubits[i] = mycircuit

qubits[0].draw()

raw_bits = list()
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.measure(0,0)
    result = execute(qubit, backend, shots=1, memory=True).result()
    bits = result.get_memory()[0]
    raw_bits.append(int(bits[0]))
print("with error correction the string was: " + frombits(raw_bits))