```
!pip3 install qiskit
```

    Requirement already satisfied: qiskit in /usr/local/lib/python3.6/dist-packages (0.19.4)
    Requirement already satisfied: qiskit-ignis==0.3.0 in /usr/local/lib/python3.6/dist-packages (from qiskit) (0.3.0)
    Requirement already satisfied: qiskit-terra==0.14.2 in /usr/local/lib/python3.6/dist-packages (from qiskit) (0.14.2)
    Requirement already satisfied: qiskit-aer==0.5.2 in /usr/local/lib/python3.6/dist-packages (from qiskit) (0.5.2)
    Requirement already satisfied: qiskit-aqua==0.7.2 in /usr/local/lib/python3.6/dist-packages (from qiskit) (0.7.2)
    Requirement already satisfied: qiskit-ibmq-provider==0.7.2 in /usr/local/lib/python3.6/dist-packages (from qiskit) (0.7.2)
    Requirement already satisfied: scipy!=0.19.1,>=0.19 in /usr/local/lib/python3.6/dist-packages (from qiskit-ignis==0.3.0->qiskit) (1.4.1)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-ignis==0.3.0->qiskit) (47.3.1)
    Requirement already satisfied: numpy>=1.13 in /usr/local/lib/python3.6/dist-packages (from qiskit-ignis==0.3.0->qiskit) (1.18.5)
    Requirement already satisfied: marshmallow<4,>=3 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (3.6.1)
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (5.4.8)
    Requirement already satisfied: retworkx>=0.3.2 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (0.3.4)
    Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (2.6.0)
    Requirement already satisfied: ply>=3.10 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (3.11)
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (1.6)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (2.8.1)
    Requirement already satisfied: marshmallow-polyfield<6,>=5.7 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (5.9)
    Requirement already satisfied: python-constraint>=1.4 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (1.4.0)
    Requirement already satisfied: fastjsonschema>=2.10 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (2.14.4)
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (0.3.1.1)
    Requirement already satisfied: networkx>=2.2; python_version > "3.5" in /usr/local/lib/python3.6/dist-packages (from qiskit-terra==0.14.2->qiskit) (2.4)
    Requirement already satisfied: cython>=0.27.1 in /usr/local/lib/python3.6/dist-packages (from qiskit-aer==0.5.2->qiskit) (0.29.20)
    Requirement already satisfied: pybind11>=2.4 in /usr/local/lib/python3.6/dist-packages (from qiskit-aer==0.5.2->qiskit) (2.5.0)
    Requirement already satisfied: pyscf; sys_platform != "win32" in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (1.7.3)
    Requirement already satisfied: docplex in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (2.14.186)
    Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (0.22.2.post1)
    Requirement already satisfied: h5py in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (2.10.0)
    Requirement already satisfied: quandl in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (3.5.0)
    Requirement already satisfied: cvxpy<1.1.0,>1.0.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (1.0.31)
    Requirement already satisfied: dlx in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (1.0.4)
    Requirement already satisfied: fastdtw in /usr/local/lib/python3.6/dist-packages (from qiskit-aqua==0.7.2->qiskit) (0.3.4)
    Requirement already satisfied: requests-ntlm>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-ibmq-provider==0.7.2->qiskit) (1.1.0)
    Requirement already satisfied: websockets<8,>=7 in /usr/local/lib/python3.6/dist-packages (from qiskit-ibmq-provider==0.7.2->qiskit) (7.0)
    Requirement already satisfied: nest-asyncio!=1.1.0,>=1.0.0 in /usr/local/lib/python3.6/dist-packages (from qiskit-ibmq-provider==0.7.2->qiskit) (1.3.3)
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.6/dist-packages (from qiskit-ibmq-provider==0.7.2->qiskit) (2.23.0)
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from qiskit-ibmq-provider==0.7.2->qiskit) (1.24.3)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.6/dist-packages (from sympy>=1.3->qiskit-terra==0.14.2->qiskit) (1.1.0)
    Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.8.0->qiskit-terra==0.14.2->qiskit) (1.12.0)
    Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.6/dist-packages (from networkx>=2.2; python_version > "3.5"->qiskit-terra==0.14.2->qiskit) (4.4.2)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.7.2->qiskit) (0.15.1)
    Requirement already satisfied: inflection>=0.3.1 in /usr/local/lib/python3.6/dist-packages (from quandl->qiskit-aqua==0.7.2->qiskit) (0.5.0)
    Requirement already satisfied: more-itertools in /usr/local/lib/python3.6/dist-packages (from quandl->qiskit-aqua==0.7.2->qiskit) (8.4.0)
    Requirement already satisfied: pandas>=0.14 in /usr/local/lib/python3.6/dist-packages (from quandl->qiskit-aqua==0.7.2->qiskit) (1.0.4)
    Requirement already satisfied: scs>=1.1.3 in /usr/local/lib/python3.6/dist-packages (from cvxpy<1.1.0,>1.0.0->qiskit-aqua==0.7.2->qiskit) (2.1.2)
    Requirement already satisfied: osqp>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from cvxpy<1.1.0,>1.0.0->qiskit-aqua==0.7.2->qiskit) (0.6.1)
    Requirement already satisfied: ecos>=2 in /usr/local/lib/python3.6/dist-packages (from cvxpy<1.1.0,>1.0.0->qiskit-aqua==0.7.2->qiskit) (2.0.7.post1)
    Requirement already satisfied: multiprocess in /usr/local/lib/python3.6/dist-packages (from cvxpy<1.1.0,>1.0.0->qiskit-aqua==0.7.2->qiskit) (0.70.9)
    Requirement already satisfied: ntlm-auth>=1.0.2 in /usr/local/lib/python3.6/dist-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.7.2->qiskit) (1.5.0)
    Requirement already satisfied: cryptography>=1.3 in /usr/local/lib/python3.6/dist-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.7.2->qiskit) (2.9.2)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.7.2->qiskit) (2020.4.5.2)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.7.2->qiskit) (2.9)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.7.2->qiskit) (3.0.4)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.14->quandl->qiskit-aqua==0.7.2->qiskit) (2018.9)
    Requirement already satisfied: future in /usr/local/lib/python3.6/dist-packages (from osqp>=0.4.1->cvxpy<1.1.0,>1.0.0->qiskit-aqua==0.7.2->qiskit) (0.16.0)
    Requirement already satisfied: cffi!=1.11.3,>=1.8 in /usr/local/lib/python3.6/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.7.2->qiskit) (1.14.0)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.6/dist-packages (from cffi!=1.11.3,>=1.8->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.7.2->qiskit) (2.20)

```
import qiskit
```

```
constant_index_dictionary = {}
constant_index_dictionary['0000'] = [0, 2]
constant_index_dictionary['0001'] = [2, 3]
constant_index_dictionary['0010'] = [0, 1]
constant_index_dictionary['0011'] = [1, 3]
constant_index_dictionary['0100'] = [2, 3]
constant_index_dictionary['0101'] = [1, 2]
constant_index_dictionary['0110'] = [0, 2]
constant_index_dictionary['0111'] = [0, 2]
constant_index_dictionary['1000'] = [0, 3]
constant_index_dictionary['1001'] = [0, 1]
constant_index_dictionary['1010'] = [1, 2]
constant_index_dictionary['1011'] = [0, 3]
constant_index_dictionary['1100'] = [1, 3]
constant_index_dictionary['1101'] = [2, 3]
constant_index_dictionary['1110'] = [1, 3]
constant_index_dictionary['1111'] = [0, 1]
```

```
import qiskit
import numpy as np
import time

CLASSICAL_REGISTER_LENGTH  = 5
QUANTUM_REGISTER_LENGTH  = 5

circuit_building_start_time = time.time()
simulator = qiskit.Aer.get_backend('qasm_simulator')
classical_register = qiskit.ClassicalRegister(CLASSICAL_REGISTER_LENGTH)
quantum_register = qiskit.QuantumRegister(QUANTUM_REGISTER_LENGTH)
circuit = qiskit.QuantumCircuit(quantum_register, classical_register)
circuit_building_end_time = time.time()

AND_gate_auxillary_qubit =  QUANTUM_REGISTER_LENGTH - 1 # last qubit as the auxillary qubit

'''
    Applies quantum AND operation to specified pair of qubits, stores the operation in AND_gate_auxillary_qubit,
    and stores the result in a classical register
    @PARAMS:
        qubit1: position of the first qubit
        qubit2: position of the second qubit
        qubit1_one: whether the first qubit is NOT
        qubit2_one: whether the second qubit is NOT
        classical_register_position: the classical register position to store the measurement of AND_gate_auxillary_qubit
'''
def AND_2_qubit(qubit1, qubit2, qubit1_one, qubit2_one, classical_register_position):

    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.ccx(quantum_register[qubit1], quantum_register[qubit2], quantum_register[AND_gate_auxillary_qubit])
    circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_position])
    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.reset(quantum_register[AND_gate_auxillary_qubit])

'''
    Applies the AND gate operation on a list of n qubits
    @PARAMS:
        qubit_list: list of qubits to perform the operation on
        qubit_one_list: whether each of those qubits is NOT
    @RETURN:
        result of the n-qubit AND operation
'''
def AND_n_qubits(qubit_list, qubit_one_list):

    length = len(qubit_list)
    if(length != len(qubit_one_list)):
        print("Incorrect dimensions")
        return
    classical_register_index = 0 # where to store pairwise AND operation results

    # handling odd number of qubits by preprocessing the last qubit
    if(length % 2 != 0):
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        circuit.cx(quantum_register[qubit_list[length - 1]], quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_index])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        classical_register_index = classical_register_index + 1
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        length = length - 1

    for index in range(length - 1, 0, -2):
        AND_2_qubit(qubit_list[index], qubit_list[index - 1], qubit_one_list[index], qubit_one_list[index - 1], classical_register_index)
        classical_register_index = classical_register_index + 1

    job = qiskit.execute(circuit, simulator, shots=1)
    result = job.result()
    counts = str(result.get_counts())
    counts = counts[counts.find('\'') + 1:]
    counts = counts[:counts.find('\'')]
    output = 1
    for index in range(0, classical_register_index, 1):
        output = output & int(counts[CLASSICAL_REGISTER_LENGTH - 1 - index])

    return output

def controlled_n_qubit_h(qubit_list, qubit_one_list):
    output = AND_n_qubits(qubit_list, qubit_one_list)
    if(output == 1):
        circuit.h(quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[0])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        job = qiskit.execute(circuit, simulator, shots=1)
        result = job.result()
        counts = str(result.get_counts())
        counts = counts[counts.find('\'') + 1:]
        counts = counts[:counts.find('\'')]
        return int(counts[len(counts) - 1])
    return 0

'''
    the main circuit for the following truth table:
    A, B, C, D = binary representation input state for the robot
    P, Q, R, S = binary representation of the output state from the robot

    New circuit in register...
'''

def main_circuit(STEPS, initial_state):
    signature = ""
    state = initial_state
    step = 0
    while (step < STEPS):

        dont_care_list = constant_index_dictionary[state]
        input_state = state
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        state = state + str(P) + str(Q) + str(R) + str(S)

        y = int(input_state, 2)^int(state,2)
        y = bin(y)[2:].zfill(len(state))
        # print("" + str(y) + " is the XOR string")
        hamming_distance = len(y.replace('0', ""))
        # print(input_state + " " + state + " " + str(hamming_distance))
        step = step + hamming_distance
        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              hidden_state = hidden_state + "x"
            else:
              hidden_state = hidden_state + state[j]

        # print(state + " " + hidden_state)
        signature = signature + hidden_state

        for _ in range(len(circuit.data)):
          circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])

    print("End state: " + str(P) + str(Q) + str(R) + str(S) )
    print("Signature: " + signature)

def initialise_starting_state(P, Q, R, S):
    if(P == 1):
      circuit.x(quantum_register[0])
    if(Q == 1):
      circuit.x(quantum_register[1])
    if(R == 1):
      circuit.x(quantum_register[2])
    if(S == 1):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def measure_time():

    total_time = 0
    for i in range(100):
        start_time = time.time()
        # output = AND_n_qubits([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
        output = controlled_n_qubit_h([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

        print(str(i) + " " + str(output))
        end_time = time.time()
        total_time = total_time + (end_time - start_time)
    print("Average time: " + str(total_time/100))

start_time = time.time()
initialise_starting_state(1, 0, 1, 1)       # message to be signed
STEPS = 20                                  # security parameter: length of the walk
main_circuit(STEPS, '1011')
# measure_time()
end_time = time.time()
print("Run in time " + str(end_time - start_time))
print(circuit_building_end_time - circuit_building_start_time)
```

    Message: 1011
    End state: 1111
    Signature: x00x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11
    Run in time 5.65201735496521
    0.0003039836883544922

```
def recipient_initialise_starting_state(P, Q, R, S):
    if(P == "1"):
      circuit.x(quantum_register[0])
    if(Q == "1"):
      circuit.x(quantum_register[1])
    if(R == "1"):
      circuit.x(quantum_register[2])
    if(S == "1"):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def recipient(message, signature, end_state):
  STEPS = len(signature)/len(end_state)
  STEPS = int(STEPS)
  index = 0
  recipient_initialise_starting_state(message[0], message[1], message[2], message[3])
  state = message
  recreated_signature = ""
  for _ in range(STEPS):

        dont_care_list = constant_index_dictionary[state]
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        if(signature[index] != "x" and signature[index] == "1"):
          P = P | 1
        elif(signature[index] != "x"):
          P = P & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          Q = Q | 1
        elif(signature[index] != "x"):
          Q = Q & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          R = R | 1
        elif(signature[index] != "x"):
          R = R & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          S = S | 1
        elif(signature[index] != "x"):
          S = S & 0

        index = index + 1

        state = "" + str(P) + str(Q) + str(R) + str(S)

        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              hidden_state = hidden_state + "x"
            else:
              hidden_state = hidden_state + state[j]

        print(state + " " + hidden_state)
        recreated_signature = recreated_signature + hidden_state

        for _ in range(len(circuit.data)):
          circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])
  print(recreated_signature)
  print(signature)
  if(recreated_signature == signature):
    print("ACCEPT")
  else:
    print("REJECT")
```

```
start = time.time()
for _ in range(len(circuit.data)):
  circuit.data.pop(0)
recipient("1011", "x10x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11", "1111")
for _ in range(len(circuit.data)):
  circuit.data.pop(0)
recipient("1011", "x00x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11", "1111")
print(time.time() - start)
```

    Message: 1011
    0101 x10x
    1011 1xx1
    0101 x10x
    1111 1xx1
    1010 xx10
    1010 1xx0
    1011 1xx1
    0011 x01x
    1000 1x0x
    1110 x11x
    0110 0x1x
    0110 x1x0
    0010 x0x0
    1111 xx11
    x10x1xx1x10x1xx1xx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11
    x10x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11
    REJECT
    Message: 1011
    0001 x00x
    1000 10xx
    1100 x10x
    1111 1x1x
    1010 xx10
    1010 1xx0
    1011 1xx1
    0011 x01x
    1000 1x0x
    1110 x11x
    0110 0x1x
    0110 x1x0
    0010 x0x0
    1111 xx11
    x00x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11
    x00x10xxx10x1x1xxx101xx01xx1x01x1x0xx11x0x1xx1x0x0x0xx11
    ACCEPT
    11.85292911529541

# Scheme 2

Non-transfer of x

More secure

Requires one-time additional sharing of a dictionary

From the two dictionaries is inferred the total number of output states
(in the cell below, 2 + 2 = 4)

```
constant_index_dictionary = {}
constant_index_dictionary['0000'] = [0, 2]
constant_index_dictionary['0001'] = [2, 3]
constant_index_dictionary['0010'] = [0, 1]
constant_index_dictionary['0011'] = [1, 3]
constant_index_dictionary['0100'] = [2, 3]
constant_index_dictionary['0101'] = [1, 2]
constant_index_dictionary['0110'] = [0, 2]
constant_index_dictionary['0111'] = [0, 2]
constant_index_dictionary['1000'] = [0, 3]
constant_index_dictionary['1001'] = [0, 1]
constant_index_dictionary['1010'] = [1, 2]
constant_index_dictionary['1011'] = [0, 3]
constant_index_dictionary['1100'] = [1, 3]
constant_index_dictionary['1101'] = [2, 3]
constant_index_dictionary['1110'] = [1, 3]
constant_index_dictionary['1111'] = [0, 1]

# additional dictionary to be shared

hidden_index_dictionary = {}
hidden_index_dictionary['0000'] = [1, 3]
hidden_index_dictionary['0001'] = [0, 1]
hidden_index_dictionary['0010'] = [2, 3]
hidden_index_dictionary['0011'] = [0, 2]
hidden_index_dictionary['0100'] = [0, 1]
hidden_index_dictionary['0101'] = [0, 3]
hidden_index_dictionary['0110'] = [1, 3]
hidden_index_dictionary['0111'] = [1, 3]
hidden_index_dictionary['1000'] = [1, 2]
hidden_index_dictionary['1001'] = [2, 3]
hidden_index_dictionary['1010'] = [0, 3]
hidden_index_dictionary['1011'] = [1, 2]
hidden_index_dictionary['1100'] = [0, 2]
hidden_index_dictionary['1101'] = [0, 1]
hidden_index_dictionary['1110'] = [0, 2]
hidden_index_dictionary['1111'] = [2, 3]
```

```
import qiskit
import numpy as np
import time

CLASSICAL_REGISTER_LENGTH  = 5
QUANTUM_REGISTER_LENGTH  = 5

circuit_building_start_time = time.time()
simulator = qiskit.Aer.get_backend('qasm_simulator')
classical_register = qiskit.ClassicalRegister(CLASSICAL_REGISTER_LENGTH)
quantum_register = qiskit.QuantumRegister(QUANTUM_REGISTER_LENGTH)
circuit = qiskit.QuantumCircuit(quantum_register, classical_register)
circuit_building_end_time = time.time()

AND_gate_auxillary_qubit =  QUANTUM_REGISTER_LENGTH - 1 # last qubit as the auxillary qubit

'''
    Applies quantum AND operation to specified pair of qubits, stores the operation in AND_gate_auxillary_qubit,
    and stores the result in a classical register
    @PARAMS:
        qubit1: position of the first qubit
        qubit2: position of the second qubit
        qubit1_one: whether the first qubit is NOT
        qubit2_one: whether the second qubit is NOT
        classical_register_position: the classical register position to store the measurement of AND_gate_auxillary_qubit
'''
def AND_2_qubit(qubit1, qubit2, qubit1_one, qubit2_one, classical_register_position):

    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.ccx(quantum_register[qubit1], quantum_register[qubit2], quantum_register[AND_gate_auxillary_qubit])
    circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_position])
    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.reset(quantum_register[AND_gate_auxillary_qubit])

'''
    Applies the AND gate operation on a list of n qubits
    @PARAMS:
        qubit_list: list of qubits to perform the operation on
        qubit_one_list: whether each of those qubits is NOT
    @RETURN:
        result of the n-qubit AND operation
'''
def AND_n_qubits(qubit_list, qubit_one_list):

    length = len(qubit_list)
    if(length != len(qubit_one_list)):
        print("Incorrect dimensions")
        return
    classical_register_index = 0 # where to store pairwise AND operation results

    # handling odd number of qubits by preprocessing the last qubit
    if(length % 2 != 0):
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        circuit.cx(quantum_register[qubit_list[length - 1]], quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_index])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        classical_register_index = classical_register_index + 1
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        length = length - 1

    for index in range(length - 1, 0, -2):
        AND_2_qubit(qubit_list[index], qubit_list[index - 1], qubit_one_list[index], qubit_one_list[index - 1], classical_register_index)
        classical_register_index = classical_register_index + 1

    job = qiskit.execute(circuit, simulator, shots=1)
    result = job.result()
    counts = str(result.get_counts())
    counts = counts[counts.find('\'') + 1:]
    counts = counts[:counts.find('\'')]
    output = 1
    for index in range(0, classical_register_index, 1):
        output = output & int(counts[CLASSICAL_REGISTER_LENGTH - 1 - index])

    return output

def controlled_n_qubit_h(qubit_list, qubit_one_list):
    output = AND_n_qubits(qubit_list, qubit_one_list)
    if(output == 1):
        circuit.h(quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[0])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        job = qiskit.execute(circuit, simulator, shots=1)
        result = job.result()
        counts = str(result.get_counts())
        counts = counts[counts.find('\'') + 1:]
        counts = counts[:counts.find('\'')]
        return int(counts[len(counts) - 1])
    return 0

'''
    the main circuit for the following truth table:
    A, B, C, D = binary representation input state for the robot
    P, Q, R, S = binary representation of the output state from the robot

    New circuit in register...
'''

def main_circuit(STEPS, initial_state):
    signature = ""
    state = initial_state
    for _ in range(STEPS):

        dont_care_list = constant_index_dictionary[state]
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        state = state + str(P) + str(Q) + str(R) + str(S)
        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              pass
            else:
              hidden_state = hidden_state + state[j]

        print(state + " " + hidden_state)
        signature = signature + hidden_state

        for _ in range(len(circuit.data)):
          circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])

    print("End state: " + str(P) + str(Q) + str(R) + str(S) )
    print("Signature: " + signature)

def initialise_starting_state(P, Q, R, S):
    if(P == 1):
      circuit.x(quantum_register[0])
    if(Q == 1):
      circuit.x(quantum_register[1])
    if(R == 1):
      circuit.x(quantum_register[2])
    if(S == 1):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def measure_time():

    total_time = 0
    for i in range(100):
        start_time = time.time()
        # output = AND_n_qubits([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
        output = controlled_n_qubit_h([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

        print(str(i) + " " + str(output))
        end_time = time.time()
        total_time = total_time + (end_time - start_time)
    print("Average time: " + str(total_time/100))

start_time = time.time()
initialise_starting_state(0, 1, 0, 1)       # message to be signed
STEPS = 10                                  # security parameter: length of the walk
main_circuit(STEPS, '0101')
# measure_time()
end_time = time.time()
print("Run in time " + str(end_time - start_time))
print(circuit_building_end_time - circuit_building_start_time)
```

    Message: 0101
    0110 00
    0010 00
    1101 01
    1001 10
    0100 00
    0110 01
    0010 00
    1100 00
    0101 00
    0110 00
    End state: 0110
    Signature: 00000110000100000000
    Run in time 2.812980890274048
    0.0001819133758544922

```
def recipient_initialise_starting_state(P, Q, R, S):
    if(P == "1"):
      circuit.x(quantum_register[0])
    if(Q == "1"):
      circuit.x(quantum_register[1])
    if(R == "1"):
      circuit.x(quantum_register[2])
    if(S == "1"):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def recipient(message, signature, end_state):

  # for every 2 bits, there are 2 additional hidden bits, by definition of the shared data structures

  STEPS = (2*len(signature))/len(end_state)
  STEPS = int(STEPS)
  index = 0
  recipient_initialise_starting_state(message[0], message[1], message[2], message[3])
  state = message
  recreated_signature = ""
  recreated_original_signature = ""
  for _ in range(STEPS):

        dont_care_list = constant_index_dictionary[state]
        hidden_index_list = hidden_index_dictionary[state]
        # print(state + " " + str(hidden_index_list))
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        for i in range(len(hidden_index_list)):
          temp_index = hidden_index_list[i]
          if(temp_index == 0):
            if(signature[index] == '1'):
              P = P | 1
            else:
              P = P & 0
          elif(temp_index == 1):
            if(signature[index] == '1'):
              Q = Q | 1
            else:
              Q = Q & 0
          elif(temp_index == 2):
            if(signature[index] == '1'):
              R = R | 1
            else:
              R = R & 0
          elif(temp_index == 3):
            if(signature[index] == '1'):
              S = S | 1
            else:
              S = S & 0
          index = index + 1

        state = "" + str(P) + str(Q) + str(R) + str(S)

        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              # hidden_state = hidden_state + "x"
              pass
            else:
              hidden_state = hidden_state + state[j]

        print(state + " " + hidden_state)
        recreated_signature = recreated_signature + hidden_state

        for _ in range(len(circuit.data)):
          circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])

  if(recreated_signature == signature and end_state == state):
    print("ACCEPT")
  else:
    print("REJECT")
```

```
start = time.time()
# for _ in range(len(circuit.data)):
#   circuit.data.pop(0)
# recipient("0101", "10011010111000010011", "1111")
for _ in range(len(circuit.data)):
  circuit.data.pop(0)
recipient("0101", "1000110000100000000", "0110")
print(time.time() - start)
```

    Message: 0101
    1110 10
    0100 00
    1110 11
    0100 00
    0010 00
    1110 10
    0100 00
    0010 00
    1100 00
    REJECT
    2.47572660446167

# k-Path dependent scheme

```
constant_index_dictionary = {}
constant_index_dictionary['0000'] = [0, 2]
constant_index_dictionary['0001'] = [2, 3]
constant_index_dictionary['0010'] = [0, 1]
constant_index_dictionary['0011'] = [1, 3]
constant_index_dictionary['0100'] = [2, 3]
constant_index_dictionary['0101'] = [1, 2]
constant_index_dictionary['0110'] = [0, 2]
constant_index_dictionary['0111'] = [0, 2]
constant_index_dictionary['1000'] = [0, 3]
constant_index_dictionary['1001'] = [0, 1]
constant_index_dictionary['1010'] = [1, 2]
constant_index_dictionary['1011'] = [0, 3]
constant_index_dictionary['1100'] = [1, 3]
constant_index_dictionary['1101'] = [2, 3]
constant_index_dictionary['1110'] = [1, 3]
constant_index_dictionary['1111'] = [0, 1]
```

```
import qiskit
import numpy as np
import time

CLASSICAL_REGISTER_LENGTH  = 5
QUANTUM_REGISTER_LENGTH  = 5

circuit_building_start_time = time.time()
simulator = qiskit.Aer.get_backend('qasm_simulator')
classical_register = qiskit.ClassicalRegister(CLASSICAL_REGISTER_LENGTH)
quantum_register = qiskit.QuantumRegister(QUANTUM_REGISTER_LENGTH)
circuit = qiskit.QuantumCircuit(quantum_register, classical_register)
circuit_building_end_time = time.time()

AND_gate_auxillary_qubit =  QUANTUM_REGISTER_LENGTH - 1 # last qubit as the auxillary qubit

'''
    Applies quantum AND operation to specified pair of qubits, stores the operation in AND_gate_auxillary_qubit,
    and stores the result in a classical register
    @PARAMS:
        qubit1: position of the first qubit
        qubit2: position of the second qubit
        qubit1_one: whether the first qubit is NOT
        qubit2_one: whether the second qubit is NOT
        classical_register_position: the classical register position to store the measurement of AND_gate_auxillary_qubit
'''
def AND_2_qubit(qubit1, qubit2, qubit1_one, qubit2_one, classical_register_position):

    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.ccx(quantum_register[qubit1], quantum_register[qubit2], quantum_register[AND_gate_auxillary_qubit])
    circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_position])
    if(qubit1_one):
        circuit.x(quantum_register[qubit1])
    if(qubit2_one):
        circuit.x(quantum_register[qubit2])
    circuit.reset(quantum_register[AND_gate_auxillary_qubit])

'''
    Applies the AND gate operation on a list of n qubits
    @PARAMS:
        qubit_list: list of qubits to perform the operation on
        qubit_one_list: whether each of those qubits is NOT
    @RETURN:
        result of the n-qubit AND operation
'''
def AND_n_qubits(qubit_list, qubit_one_list):

    length = len(qubit_list)
    if(length != len(qubit_one_list)):
        print("Incorrect dimensions")
        return
    classical_register_index = 0 # where to store pairwise AND operation results

    # handling odd number of qubits by preprocessing the last qubit
    if(length % 2 != 0):
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        circuit.cx(quantum_register[qubit_list[length - 1]], quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[classical_register_index])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        classical_register_index = classical_register_index + 1
        if(qubit_one_list[length - 1] == 1):
            circuit.x(quantum_register[qubit_list[length-1]])
        length = length - 1

    for index in range(length - 1, 0, -2):
        AND_2_qubit(qubit_list[index], qubit_list[index - 1], qubit_one_list[index], qubit_one_list[index - 1], classical_register_index)
        classical_register_index = classical_register_index + 1

    job = qiskit.execute(circuit, simulator, shots=1)
    result = job.result()
    counts = str(result.get_counts())
    counts = counts[counts.find('\'') + 1:]
    counts = counts[:counts.find('\'')]
    output = 1
    for index in range(0, classical_register_index, 1):
        output = output & int(counts[CLASSICAL_REGISTER_LENGTH - 1 - index])

    return output

def controlled_n_qubit_h(qubit_list, qubit_one_list):
    output = AND_n_qubits(qubit_list, qubit_one_list)
    if(output == 1):
        circuit.h(quantum_register[AND_gate_auxillary_qubit])
        circuit.measure(quantum_register[AND_gate_auxillary_qubit], classical_register[0])
        circuit.reset(quantum_register[AND_gate_auxillary_qubit])
        job = qiskit.execute(circuit, simulator, shots=1)
        result = job.result()
        counts = str(result.get_counts())
        counts = counts[counts.find('\'') + 1:]
        counts = counts[:counts.find('\'')]
        return int(counts[len(counts) - 1])
    return 0

'''
    the main circuit for the following truth table:
    A, B, C, D = binary representation input state for the robot
    P, Q, R, S = binary representation of the output state from the robot

    New circuit in register...
'''

def main_circuit(STEPS, initial_state):
    signature = ""
    state = initial_state
    used_states = []
    step = 0
    rollback_count = 0
    while True:
        if(step == STEPS):
          break
        dont_care_list = constant_index_dictionary[state]
        rollback_state = state
        if(state not in used_states):
          used_states.append(state)
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        state = state + str(P) + str(Q) + str(R) + str(S)
        if(state in used_states):
          rollback_count = rollback_count + 1
          if(rollback_count == (len(initial_state) + 10)):
            print("Aborting.")
            return "ABORT"
          P = rollback_state[0]
          Q = rollback_state[1]
          R = rollback_state[2]
          S = rollback_state[3]
          state = rollback_state
          for _ in range(len(circuit.data)):
            circuit.data.pop(0)

          if(P == '1'):
            print("Rollback reset")
            circuit.x(quantum_register[0])

          if(Q == '1'):
            print("Rollback reset")
            circuit.x(quantum_register[1])

          if(R == '1'):
            print("Rollback reset")
            circuit.x(quantum_register[2])

          if(S == '1'):
            print("Rollback reset")
            circuit.x(quantum_register[3])
          print("Rolling back")
          continue

        step = step + 1
        rollback = 0
        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              hidden_state = hidden_state + "x"
            else:
              hidden_state = hidden_state + state[j]

        signature = signature + hidden_state
        # print(state + " " + hidden_state)

        for _ in range(len(circuit.data)):
            circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])
    return signature

def initialise_starting_state(P, Q, R, S):
    for _ in range(len(circuit.data)):
      circuit.data.pop(0)
    if(P == 1):
      circuit.x(quantum_register[0])
    if(Q == 1):
      circuit.x(quantum_register[1])
    if(R == 1):
      circuit.x(quantum_register[2])
    if(S == 1):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def measure_time():

    total_time = 0
    for i in range(100):
        start_time = time.time()
        # output = AND_n_qubits([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
        output = controlled_n_qubit_h([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

        print(str(i) + " " + str(output))
        end_time = time.time()
        total_time = total_time + (end_time - start_time)
    print("Average time: " + str(total_time/100))

```

Creating a long message

100 *bits*

```
def create_random_message(NUMBER_OF_BITS):
  message = ""
  c = qiskit.ClassicalRegister(1)
  q = qiskit.QuantumRegister(1)
  s = qiskit.Aer.get_backend('qasm_simulator')

  for i in range(NUMBER_OF_BITS):
    print(i)
    random_circuit = qiskit.QuantumCircuit(q, c)
    random_circuit.h(q[0])
    random_circuit.measure(q[0], c[0])

    job = qiskit.execute(random_circuit, s, shots=1)
    result = job.result()
    counts = str(result.get_counts())
    counts = counts[counts.find('\'') + 1:]
    counts = counts[:counts.find('\'')]
    message = message + counts

  print(message)

create_random_message(100)
```

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    1011000001011010110011111011011100111001000010001111011101101100010100100011010010111000110101100011

Signing a long message

```
def sign_message(message):
  signature = ""
  ITER = int(len(message)/4)
  start_time = time.time()
  STEPS = 5                                  # security parameter: length of the walk
  iter = 0
  while True:
    if(iter == ITER):
      break
    state = message[0:4]
    initialise_starting_state(int(state[0]), int(state[1]), int(state[2]), int(state[3]))
    return_signature = main_circuit(STEPS, state)
    if(return_signature == "ABORT"):
      print("Rerun")
      continue
    iter = iter + 1
    signature = signature + return_signature
    message = message[4:]
  end_time = time.time()
  print("Run in time " + str(end_time - start_time))

  print(signature)

sign_message('1011000001011010110011111011011100111001000010001111011101101100010100100011010010111000110101100011')
```

    Message: 1011
    Rollback reset
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0000
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0101
    Message: 1010
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rolling back
    Message: 1100
    Rollback reset
    Rolling back
    Message: 1111
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 1011
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0111
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0011
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Message: 1001
    Message: 0000
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 1000
    Message: 1111
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0111
    Message: 0110
    Message: 1100
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0101
    Message: 0010
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0011
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0100
    Message: 1011
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Message: 1000
    Message: 1101
    Message: 0110
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rollback reset
    Rolling back
    Rollback reset
    Rolling back
    Rollback reset
    Rolling back
    Rollback reset
    Rolling back
    Rollback reset
    Rollback reset
    Rolling back
    Message: 0011
    Run in time 74.02919340133667
    x11xx1x1xx01xx10x0x1x1x01x0x10xxxx10x1x10xx1x1x1xx11x00x00xx0xx0xx11xx00x10x1x0x0x0x1xx1xx00x11x0x0xxx11x11xx1x00x1xx0x1x01x1x0xx01x0xx0xx01x1x1xx00x10x0x0x1xx00x0xx1x101xx0xx0x1x1xx10x1x1x0x1x01x1x0xx1x101xx1xx1xx00x10xx01x1xx1x10x0xx0x0x0xx01xx10x1x1x0x1x00xx0x01xx1x00x11xx1x1xx0x10x0xx1x01x0x10xx1x1xxx00x01x0xx10x1x1xx00x1xx0x10x1xxx11xx0100xx10xxx11x0x0x0x1xxx101x0x1x1xxx0010xx0xx10x1xxx11xx01x10x0xx0x0x0xx0110xxx01x0xx10x0xx0x1xx0000xx10xxx11x0x1xx1x1x0x0xx100x0x10xx0xx10x1xxx100x1xx1x1x1x1

```
print(len('x00x10xxx10x1x1xxx01'))
```

    20

```
def recipient_initialise_starting_state(P, Q, R, S):
    for _ in range(len(circuit.data)):
      circuit.data.pop(0)
    if(P == "1"):
      circuit.x(quantum_register[0])
    if(Q == "1"):
      circuit.x(quantum_register[1])
    if(R == "1"):
      circuit.x(quantum_register[2])
    if(S == "1"):
      circuit.x(quantum_register[3])
    print("Message: " + str(P) + str(Q) + str(R) + str(S))

def recipient(message, signature, end_state):
  STEPS = len(signature)/len(end_state)
  STEPS = int(STEPS)
  index = 0
  recipient_initialise_starting_state(message[0], message[1], message[2], message[3])
  state = message
  recreated_signature = ""
  for _ in range(STEPS):

        dont_care_list = constant_index_dictionary[state]
        state = ""

        P = controlled_n_qubit_h([0, 1, 3], [1, 1, 0]) | controlled_n_qubit_h([1, 2], [0, 1]) | controlled_n_qubit_h([0, 2, 3], [0, 0, 1]) | AND_n_qubits([1, 2, 3], [0, 0, 0]) | AND_n_qubits([0, 1, 3], [1, 1, 1]) | AND_n_qubits([1, 2, 3], [1, 1, 1])

        Q = controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 1, 1]) | controlled_n_qubit_h([0, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([1, 2, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 0, 1, 0]) | controlled_n_qubit_h([0, 1, 2, 3], [0, 1, 0, 0]) | AND_n_qubits([0, 1, 3], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 1, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [1, 0, 1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 1, 0])

        R = controlled_n_qubit_h([0, 1, 2], [1, 1, 0]) | controlled_n_qubit_h([0, 1, 2], [0, 0, 0]) | controlled_n_qubit_h([0, 1, 3], [0, 1, 0]) | controlled_n_qubit_h([0, 2, 3], [0, 1, 1]) | AND_n_qubits([0, 1], [1, 0]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 1])

        S = controlled_n_qubit_h([1, 2, 3], [1, 0, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 1, 1]) | controlled_n_qubit_h([0, 1, 3], [1, 0, 0]) | controlled_n_qubit_h([0, 1, 2], [1, 0, 0]) | controlled_n_qubit_h([1, 2, 3], [0, 0, 0]) |  AND_n_qubits([0, 1, 2], [0, 0, 1]) | AND_n_qubits([0, 1, 2, 3], [0, 1, 0, 0])

        if(signature[index] != "x" and signature[index] == "1"):
          P = P | 1
        elif(signature[index] != "x"):
          P = P & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          Q = Q | 1
        elif(signature[index] != "x"):
          Q = Q & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          R = R | 1
        elif(signature[index] != "x"):
          R = R & 0

        index = index + 1

        if(signature[index] != "x" and signature[index] == "1"):
          S = S | 1
        elif(signature[index] != "x"):
          S = S & 0

        index = index + 1

        state = "" + str(P) + str(Q) + str(R) + str(S)

        hidden_state = ""
        for j in range(len(state)):
            if(j in dont_care_list):
              hidden_state = hidden_state + "x"
            else:
              hidden_state = hidden_state + state[j]

        recreated_signature = recreated_signature + hidden_state
        print(state + " " + hidden_state)
        for _ in range(len(circuit.data)):
          circuit.data.pop(0)

        if(P == 1):
            circuit.x(quantum_register[0])

        if(Q == 1):
            circuit.x(quantum_register[1])

        if(R == 1):
            circuit.x(quantum_register[2])

        if(S == 1):
            circuit.x(quantum_register[3])
  print(recreated_signature)
  print(signature)
  if(recreated_signature == signature):
    print("ACCEPT")
  else:
    print("REJECT")
  return recreated_signature

```

```
import time
start = time.time()
for _ in range(len(circuit.data)):
  circuit.data.pop(0)
STEPS = int(len('1011000001011010110011111011011100111001000010001111011101101100010100100011010010111000110101100011') / 4)
message = '1011000001011010110011111011011100111001000010001111011101101100010100100011010010111000110101100011'
signature = 'x11xx1x1xx01xx10x0x1x1x01x0x10xxxx10x1x10xx1x1x1xx11x00x00xx0xx0xx11xx00x10x1x0x0x0x1xx1xx00x11x0x0xxx11x11xx1x00x1xx0x1x01x1x0xx01x0xx0xx01x1x1xx00x10x0x0x1xx00x0xx1x101xx0xx0x1x1xx10x1x1x0x1x01x1x0xx1x101xx1xx1xx00x10xx01x1xx1x10x0xx0x0x0xx01xx10x1x1x0x1x00xx0x01xx1x00x11xx1x1xx0x10x0xx1x01x0x10xx1x1xxx00x01x0xx10x1x1xx00x1xx0x10x1xxx11xx0100xx10xxx11x0x0x0x1xxx101x0x1x1xxx0010xx0xx10x1xxx11xx01x10x0xx0x0x0xx0110xxx01x0xx10x0xx0x1xx0000xx10xxx11x0x1xx1x1x0x0xx100x0x10xx0xx10x1xxx100x1xx1x1x1x1'
temp_signature = signature
k = int(len(signature)/len(message))
end_index = k*4
recipient_signature = ""
for _ in range(STEPS):
  start_state = message[0:4]
  message = message[4:]
  mess_signature = signature[0:end_index]
  signature = signature[end_index:]
  recipient_signature = recipient_signature + recipient(start_state, mess_signature, '0000')

if(recipient_signature == temp_signature):
  print("ACCEPT")
else:
  print("REJECT")
print(time.time() - start)
```

```
1111 1xx1 1111 1xx1
1011 xx11 1011 xx11
Rolling back
1101 x10x 0101 x10x
0001 00xx 0010 0xx0
1000 10xx 1010 xx10

```

<pre style="word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: "Courier New",Courier,monospace">      ┌───┐
q5_0: ┤ X ├
      └───┘
q5_1: ─────
      ┌───┐
q5_2: ┤ X ├
      ├───┤
q5_3: ┤ X ├
      └───┘
q5_4: ─────

c5_0: ═════

c5_1: ═════

c5_2: ═════

c5_3: ═════

c5_4: ═════
           </pre>

```
print(recipient('1011', 'x11xxx01xx10x0x0xx0100xx11xx1x1xxx11x11x', '0000'))
print(recipient('1001', 'xx001x1xxx01xx0011xx1x0x0x1xx1x1xx100xx0', '0000'))
```

    Message: 1011
    x11x0x0x1xx00x1xxx0100xx11xx1x1xxx11x11x
    x11xxx01xx10x0x0xx0100xx11xx1x1xxx11x11x
    REJECT
    x11x0x0x1xx00x1xxx0100xx11xx1x1xxx11x11x
    Message: 1001
    xx00x11x0x0x0xx011xx1x0x0x1xx1x1xx100xx0
    xx001x1xxx01xx0011xx1x0x0x1xx1x1xx100xx0
    REJECT
    xx00x11x0x0x0xx011xx1x0x0x1xx1x1xx100xx0

```

```