import os
import sys

from IPython.display import Image

from qiskit import QuantumProgram
from qiskit.tools.visualization import plot_histogram

def show_image(img_name):
    """Display an image in the notebook.
    
    This is set to the default path of the root of the tutorials repo
    images directory in the sub-folder 'intro_img'.
    
    Args:
        img_name (str): file name to display
    """
    return Image(filename=os.path.join("..", "images", "intro_img", img_name))

def execute_and_plot(qp, circuits):
    """Execute and plot the histograms with default settings.
    
    Args:
        qp: QuantumProgram containing the circuits
        circuits (list): list of circuits to execute
    """
    results = qp.execute(circuits)
    
    for circuit in circuits:
        print(circuit)
        plot_histogram(results.get_counts(circuit))

qp = QuantumProgram()

n = 1  # number of qubits
q = qp.create_quantum_register("q", n)
c = qp.create_classical_register("c", n)

show_image("single_q_x_gate.png")

single_x = qp.create_circuit("single_x", [q], [c])
single_x.x(q[0])
single_x.measure(q[0], c[0])
execute_and_plot(qp, ["single_x"])

show_image("single_q_h_gate_0.png")

single_h = qp.create_circuit("single_h", [q], [c])
single_h.h(q[0])
single_h.measure(q[0], c[0])
execute_and_plot(qp, ["single_h"])

show_image("single_q_h_gate_1.png")

single_xh = qp.create_circuit("single_xh", [q], [c])
single_xh.x(q[0])
single_xh.h(q[0])
single_xh.measure(q[0], c[0])
execute_and_plot(qp, ["single_xh"])

show_image("single_q_h_gate_2.png")

# This is the |+> state
single_hh = qp.create_circuit("single_hh", [q], [c])
single_hh.h(q[0])
single_hh.h(q[0])
single_hh.measure(q[0], c[0])
execute_and_plot(qp, ["single_hh"])

show_image("single_q_h_gate_3.png")

# This is the |-> state
single_xhh = qp.create_circuit("single_xhh", [q], [c])
single_xhh.x(q[0])
single_xhh.h(q[0])
single_xhh.h(q[0])
single_xhh.measure(q[0],c[0])
execute_and_plot(qp, ["single_xhh"])

show_image("single_q_z_gate_0.png")

single_z = qp.create_circuit("single_z", [q], [c])
single_z.z(q[0])
single_z.measure(q[0],c[0])
execute_and_plot(qp, ["single_z"])

show_image("single_q_z_gate_1.png")

single_zh = qp.create_circuit("single_zh", [q], [c])
single_zh.h(q[0])
single_zh.z(q[0])
single_zh.measure(q[0],c[0])
execute_and_plot(qp, ["single_zh"])

show_image("single_q_z_gate_tbl.png")

# 0 rotation around z, Pr(0) = 1.0
phs_0 = qp.create_circuit("phs_0", [q], [c])
phs_0.h(q[0])
phs_0.h(q[0])  # for x-basis measurement
phs_0.measure(q[0], c[0])

# pi/4 rotation around z, Pr(0) = 0.85
phs_pi4 = qp.create_circuit("phs_pi4", [q], [c])
phs_pi4.h(q[0])
phs_pi4.t(q[0])
phs_pi4.h(q[0])  # for x-basis measurement
phs_pi4.measure(q[0], c[0])

# pi/2 rotation around z, Pr(0) = 0.5
phs_pi2 = qp.create_circuit("phs_pi2", [q], [c])
phs_pi2.h(q[0])
phs_pi2.s(q[0])
# Alternate example gate:
#phs_pi2.sdg(q[0])  #rotation -pi/2 using sdg instead of s
phs_pi2.h(q[0])  # for x-basis measurement
phs_pi2.measure(q[0], c[0])

# 3pi/4 rotation around z, Pr(0) = 0.15
phs_3pi4 = qp.create_circuit("phs_3pi4", [q], [c])
phs_3pi4.h(q[0])
phs_3pi4.s(q[0])
phs_3pi4.t(q[0])
phs_3pi4.h(q[0])  # for x-basis measurement
phs_3pi4.measure(q[0], c[0])

# pi rotation around z, Pr(0) = 0
phs_pi = qp.create_circuit("phs_pi", [q], [c])
phs_pi.h(q[0])
phs_pi.z(q[0])
phs_pi.h(q[0])  # for measurement
phs_pi.measure(q[0], c[0])

execute_and_plot(qp, ["phs_0", "phs_pi4", "phs_pi2",
                      "phs_3pi4", "phs_pi"])

n = 2  # number of qubits
q2 = qp.create_quantum_register("q2", n)
c2 = qp.create_classical_register("c2", n)

show_image("multi_q_0.png")

# |00> -- |00>
cnot_00 = qp.create_circuit("cnot_00", [q2], [c2])
cnot_00.cx(q2[0], q2[1])
cnot_00.measure(q2[0], c2[0])
cnot_00.measure(q2[1], c2[1])
execute_and_plot(qp, ["cnot_00"])

show_image("multi_q_1.png")

# |01> -- |11>
cnot_01 = qp.create_circuit("cnot_01", [q2], [c2])
cnot_01.x(q2[0])
cnot_01.cx(q2[0], q2[1])
cnot_01.measure(q2[0], c2[0])
cnot_01.measure(q2[1], c2[1])
execute_and_plot(qp, ["cnot_01"])

show_image("multi_q_2.png")

# |10> -- |10>
cnot_10 = qp.create_circuit("cnot_10", [q2], [c2])
cnot_10.x(q2[1])
cnot_10.cx(q2[0], q2[1])
cnot_10.measure(q2[0], c2[0])
cnot_10.measure(q2[1], c2[1])
execute_and_plot(qp, ["cnot_10"])

show_image("multi_q_3.png")

# |11> -- |01>
cnot_11 = qp.create_circuit("cnot_11", [q2], [c2])
cnot_11.x(q2[0])
cnot_11.x(q2[1])
cnot_11.cx(q2[0], q2[1])
cnot_11.measure(q2[0], c2[0])
cnot_11.measure(q2[1], c2[1])
execute_and_plot(qp, ["cnot_11"])

show_image("ent_q_0.png")

e0 = qp.create_circuit("e0", [q2], [c2])
e0.h(q2[0])  # apply H-gate for superposition to q0
e0.cx(q2[0], q2[1])  # apply CNOT control from q0 to q1
e0.measure(q2[0], c2[0])
e0.measure(q2[1], c2[1])
execute_and_plot(qp, ["e0"])

show_image("ent_q_1.png")

e1 = qp.create_circuit("e1", [q2], [c2])
e1.h(q2[0])  # apply H-gate on q0 for superposition
e1.x(q2[1])  # apply x-gate on q1
e1.cx(q2[0], q2[1])  # apply CNOT control from q0 to q1
e1.measure(q2[0], c2[0])
e1.measure(q2[1], c2[1])
execute_and_plot(qp, ["e1"])

