# Checking the version of PYTHON; we only support 3 at the moment
import sys
if sys.version_info < (3,0):
    raise Exception('Please use Python version 3 or greater.')
    
# useful additional packages 
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import time
from pprint import pprint

# importing the QISKit
from qiskit import QuantumCircuit, QuantumProgram 
#import Qconfig   
# import basic plot tools
from qiskit.tools.visualization import plot_histogram

Q_program = QuantumProgram()
#Q_program.set_api(Qconfig.APItoken, Qconfig.config['url'])  
"Choice of the backend"
backend = 'local_qasm_simulator' #can be very slow when number of shoots increases
#backend = 'ibmqx_hpc_qasm_simulator'   
print("Your choice for the backend is: ", backend)

# Define a F_gate
def F_gate(circ,q,i,j,n,k) :
    theta = np.arccos(np.sqrt(1/(n-k+1)))
    circ.ry(-theta,q[j])       
    circ.cz(q[i],q[j])
    circ.ry(theta,q[j])
    circ.barrier(q[i])
# Define the cxrv gate which uses reverse CNOT instead of CNOT
def  cxrv(circ,q,i,j) :
    circ.h(q[i])
    circ.h(q[j])
    circ.cx(q[j],q[i])
    circ.h(q[i])
    circ.h(q[j])
    circ.barrier(q[i],q[j])

#"CIRCUITS"

q = Q_program.create_quantum_register("q", 16)
c = Q_program.create_classical_register("c", 16)
twin = Q_program.create_circuit("twin", [q], [c])
# First W state
twin.x(q[14])
F_gate(twin,q,14,3,3,1)
F_gate(twin,q,3,2,3,2)
twin.cx(q[3],q[14])
twin.cx(q[2],q[3])

#Second W state
twin.x(q[12])
F_gate(twin,q,12,5,3,1)
F_gate(twin,q,5,6,3,2)
cxrv(twin,q,5,12)
twin.cx(q[6],q[5])

#Coin tossing
twin.h(q[0])
twin.h(q[1])

switch1 = Q_program.create_circuit('switch1',[q],[c])
#Stick or switch
switch1.h(q[13])

for i in range (4) :
    switch1.measure(q[i] , c[i]);
for i in range (5,7) :
    switch1.measure(q[i] , c[i]);
for i in range (12,15) :
    switch1.measure(q[i] , c[i]);

Q_program.add_circuit("AliceBob", twin+switch1)
Label = ["left", "central", "right"]
wstates = 0

while wstates != 1:
    time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
    print("Alice vs Bob", "backend=", backend, "starting time", time_exp)
    result = Q_program.execute("AliceBob", backend=backend, shots=1, max_credits=5, wait=5, timeout=120)
    time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
    print("Alice vs Bob", "backend=", backend, "     end time", time_exp)
    
    cstr = str(result.get_counts("AliceBob"))
    nb_of_cars = int(cstr[3]) + int(cstr[14]) + int(cstr[15])
    nb_of_doors = int(cstr[12]) + int(cstr[11]) + int(cstr[5])
    wstates = nb_of_cars * nb_of_doors

print(" ")
print('Alice:       One car and two goats are now hidden behind these doors.')
print('             Which door do you choose?')
print(" ")

"Chosing the left door"
if int(cstr[5]) == 1:
    Doorchosen = 1

"Chosing the center door"
if int(cstr[11]) == 1:   
    Doorchosen = 2
    
"Chosing the right door"
if int(cstr[12]) == 1:
    Doorchosen = 3

time.sleep(2)    
print('Bob:         My choice is the',Label[Doorchosen-1], "door")
print(" ")

randomnb = int(cstr[16]) + int(cstr[17]) %2

if cstr[3] == "1":    #car behind left door 
    Doorwinning = 1
    if Doorchosen == 1: 
        Dooropen = 2 + randomnb
        Doorswitch = 3 - randomnb
    if Doorchosen == 2:
        Dooropen = 3 
        Doorswitch = 1
    if Doorchosen == 3:
        Dooropen = 2
        Doorswitch = 1    
               
if cstr[14] == "1":     #car behind central door 
    Doorwinning = 2
    if Doorchosen == 2:
        Dooropen = 1 + 2*randomnb 
        Doorswitch = 3 - 2*randomnb 
    if Doorchosen == 1: 
        Dooropen = 3
        Doorswitch = 2
    if Doorchosen == 3:   
        Dooropen = 1
        Doorswitch = 2
        
if cstr[15] == "1":     #car behind right door 
    Doorwinning = 3
    if Doorchosen == 3:
        Dooropen = randomnb + 1
        Doorswitch = 2 - randomnb
    if Doorchosen == 1: 
        Dooropen = 2
        Doorswitch = 3
    if Doorchosen == 2:   
        Dooropen = 1
        Doorswitch = 3
        
time.sleep(2)           
print('Alice:       Now I open the', Label[Dooropen-1], 'door and you see a goat')
time.sleep(2)
print('             You get an opportunity to change your choice!')
time.sleep(2)
print('             Do you want to switch for the',Label[Doorswitch-1], "door?")
print(" ")
time.sleep(2)

switch_flag = int(cstr[4])
    
"BOB STICKS WITH HIS FIRST CHOICE!"
if switch_flag == 0:
    Doorfinal = Doorchosen
    print('Bob:         I stick with my first choice, the',Label[Doorfinal-1], "door")
                
"BOB CHANGES HIS MIND!"
if switch_flag == 1:
    Doorfinal = Doorswitch
    print('Bob:         I change my mind and choose the',Label[Doorfinal-1], "door")

"FINAL ANNOUNCE"
if Doorfinal == Doorwinning:
    endmessage = 'won the car! Congratulations!'
else:
    endmessage = 'won a goat! Sorry!'

time.sleep(2)
print()
print('Alice:       You opened the',Label[Doorfinal-1],'door and', endmessage)
    
print("Game over")

#Toffoli gates
Toffoli = Q_program.create_circuit('Toffoli',[q],[c])
Toffoli.ccx(q[3], q[5], q[4])
Toffoli.swap(q[2],q[3])
Toffoli.swap(q[6],q[5])
Toffoli.ccx(q[3], q[5], q[4])
Toffoli.swap(q[3],q[14])
Toffoli.swap(q[12],q[5])
Toffoli.ccx(q[3], q[5], q[4])

# A general solution with 50% switching strategy
switch_fifty_percent = Q_program.create_circuit('switch_fifty_percent',[q],[c])
#switch flag
switch_fifty_percent.h(q[13])
switch_fifty_percent.cx(q[13],q[4])
switch_fifty_percent.measure(q[4] , c[4]);
switch_fifty_percent.measure(q[13] , c[13]);
Q_program.add_circuit("general_solution", twin+Toffoli+switch_fifty_percent)
circuits = ['general_solution']
shots = 1024
time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
print(backend, "shots",  shots, "starting time", time_exp)
result = Q_program.execute(circuits, backend=backend, shots=shots, max_credits=5, wait=30, timeout=600)
time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
print(backend, "shots",  shots, "end time", time_exp)
plot_histogram(result.get_counts("general_solution"))
print(result.get_counts("general_solution"))
observable_stickwon =  {'0000000000010000': 1, '0010000000010000': 0, '0010000000000000': 0, '0000000000000000': 0}
observable_switchwon = {'0000000000010000': 0, '0010000000010000': 1, '0010000000000000': 0, '0000000000000000': 0}
observable_stickall =  {'0000000000010000': 1, '0010000000010000': 0, '0010000000000000': 0, '0000000000000000': 1}
observable_switchall = {'0000000000010000': 0, '0010000000010000': 1, '0010000000000000': 1, '0000000000000000': 0}
stickwon = result.average_data("general_solution",observable_stickwon)
switchwon = result.average_data("general_solution",observable_switchwon)
stickall = result.average_data("general_solution",observable_stickall)
switchall = result.average_data("general_solution",observable_switchall)
print("Proportion sticking: %6.2f " % stickall)
print("Proportion switching: %6.2f " % switchall)
stickwon_stickall = stickwon/stickall
switchwon_switchall = switchwon/switchall
print("Proportion winning when sticking:  %6.2f " % stickwon_stickall)
print("Proportion winning when switching: %6.2f " % switchwon_switchall)

# Illustrating different strategies
xdat = []
ydat = []
observable = {'0000000000000000': 0, '0000000000010000': 1}
shots = 1024
time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
print(backend, "shots",  shots, "starting time", time_exp)

for i in range(9) :
    strategies = Q_program.create_circuit('strategies',[q],[c])
    Prob = i/8
    lambda_s = 2*np.arcsin(np.sqrt(Prob))
    strategies.rx(lambda_s,q[13])
    strategies.cx(q[13],q[4])
    strategies.measure(q[4] , c[4]);
    statploti = "statplot"+str(i)
    Q_program.create_circuit("statploti",[q],[c])
    Q_program.add_circuit("statploti",twin+Toffoli+strategies)
    result = Q_program.execute("statploti", backend=backend, shots=shots, max_credits=5, wait=30, timeout=600)
    loop_average=(result.average_data("statploti",observable))
    print(statploti," Proportion switching: %6.3f" % Prob, "  Proportion winning: %6.2f" % loop_average)
    ydat.append(loop_average)
    xdat.append(Prob)
    
time_exp = time.strftime('%d/%m/%Y %H:%M:%S')
print(backend, "shots",  shots, "end time", time_exp)   
plt.plot(xdat, ydat, 'ro')
plt.grid()
plt.ylabel('Probability of Winning', fontsize=12)
plt.xlabel(r'Probability of Switching', fontsize=12)
plt.show()
print("Our Advice: \n")
y_aver = []
for j in range(0,7,3) :
    y_aver.append((ydat[j] + ydat[j+1] +ydat[j+2])/3)

if y_aver[0] == max(y_aver) :
    print("                  Thou Shalt Not Switch") 

elif y_aver[2] == max(y_aver) :
    print("                  Thou Shalt Not Stick")
    
else:
    print("          Just follow the intuition of the moment")  

%run "../version.ipynb"

