# importing Qiskit
from qiskit import IBMQ, BasicAer
from qiskit.providers.ibmq import least_busy
from qiskit import QuantumCircuit, execute

# import basic plot tools
from qiskit.visualization import plot_histogram
from qiskit_textbook.tools import simon_oracle

b = '110'

n = len(b) #=> 3
simon_circuit = QuantumCircuit(n*2, n) #=> QuantumCircuit(6, 3)

# 最初の３量子ビットにアダマール変換する
simon_circuit.h(range(n))    
    
# Apply barrier for visual separation
simon_circuit.barrier()

# ビット列b に従って動く f (x) のオラクルを回路に追加する
simon_circuit += simon_oracle(b)

# Apply barrier for visual separation
simon_circuit.barrier()

# 最初の３量子ビットにアダマール変換する
simon_circuit.h(range(n))

# 測定する
simon_circuit.measure(range(n), range(n))
simon_circuit.draw()

# use local simulator
backend = BasicAer.get_backend('qasm_simulator')
shots = 1024
results = execute(simon_circuit, backend=backend, shots=shots).result()
counts = results.get_counts()
plot_histogram(counts)

# 結果に対して、ドット積を計算する。
def bdotz(b, z):
    accum = 0
    for i in range(len(b)):
        accum += int(b[i]) * int(z[i])
    return (accum % 2)

for z in counts:
    print( '{}.{} = {} (mod 2)'.format(b, z, bdotz(b,z)) )


b = '11'
n = len(b)
simon_circuit_2 = QuantumCircuit(n*2, n)

# 最初の2量子ビットにアダマール変換する
simon_circuit_2.h(range(n))

# ビット列b に従って動く f (x) のオラクルを回路に追加する
simon_circuit_2 += simon_oracle(b)

# 最初の2量子ビットにアダマール変換する
simon_circuit_2.h(range(n))

# 測定する
simon_circuit_2.measure(range(n), range(n))
simon_circuit_2.draw()

# Load our saved IBMQ accounts and get the least busy backend device with less than or equal to 5 qubits
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
backend = least_busy(provider.backends(filters=lambda x: x.configuration().n_qubits >= n and 
                                   not x.configuration().simulator and x.status().operational==True))
print("least busy backend: ", backend)

# Execute and monitor the job
from qiskit.tools.monitor import job_monitor
shots = 1024
job = execute(simon_circuit_2, backend=backend, shots=shots, optimization_level=3)
job_monitor(job, interval = 2)

# Get results and plot counts
device_counts = job.result().get_counts()
plot_histogram(device_counts)

# 結果に対して、ドット積を計算する。
def bdotz(b, z):
    accum = 0
    for i in range(len(b)):
        accum += int(b[i]) * int(z[i])
    return (accum % 2)

print('b = ' + b)
for z in device_counts:
    print( '{}.{} = {} (mod 2) ({:.1f}%)'.format(b, z, bdotz(b,z), device_counts[z]*100/shots))