# """Tools for constructing quantum circuits."""
import json
import warnings
from math import pi

import cirq
import numpy as np
import pyquil
import qiskit
from pyquil import Program
from pyquil.gates import MEASURE, I
from pyquil.quilatom import quil_cos, quil_sin

from ..utils import SCHEMA_VERSION
from ._gate import Gate
from ._gateset import COMMON_GATES, UNIQUE_GATES
from ._qubit import Qubit

class Circuit(object):
    """Base class for quantum circuits.

    Attributes:
        name: string
            Name of the Circuit object. By default this is called 'Unnamed'.
        gates: list[Gate]
            The gate sequence of the circuit. Implemented as a list of core.gate.Gate
            objects.
        qubits: list[Qubit]
            The set of qubits that the circuit acts on. Implemented as a list of
            core.qubit.Qubit objects.
        info: dictionary
            Additional information related to the circuit. For example if the circuit is
            converted from another package, information related to the native
            specification of the circuit in that package is recorded here.
    """

    def __init__(self, input_object=None, name="Unnamed"):
        """Initialize a circuit. Most likely the circuit is generated by converting a
        circuit object in other packages to core.circuit.Circuit object.

        Args:
            input_object: pyquil.Program, cirq.Circuit, qiskit.QuantumCircuit
                A generic circuit object that may be created from one of the various
                packages currently supported by Zap OS.
        """

        self.name = name  # name of the circuit (by default a random Hash string)
        self.gates = []  # list of gates (see gate.py for Gate class def)
        self.qubits = []  # list of qubits (see qubit.py for Qubit class def)
        self.info = {
            "label": None  # the name of the native package that generates the circuit
            # e.g. 'pyquil', 'cirq', 'qiskit' etc. The purpose is to
            # provide hints about what unique functionalities of
            # the package one might be able to take advantage of.
        }

        if isinstance(input_object, pyquil.Program):
            self.from_pyquil(input_object)
        elif isinstance(input_object, pyquil.quilbase.Gate):
            converted_input = pyquil.Program(input_object)
            self.from_pyquil(converted_input)
        elif isinstance(input_object, cirq.Circuit):
            self.from_cirq(input_object)
        elif isinstance(input_object, qiskit.QuantumCircuit):
            self.from_qiskit(input_object)
        elif input_object is None:
            pass
        else:
            raise (
                TypeError(
                    "Incorrect type of input object: {0}".format(type(input_object))
                )
            )

    @property
    def n_multiqubit_gates(self):
        """The number of multiqubit gates in the circuit."""

        n_mq_gates = 0
        for gate in self.gates:
            if len(gate.qubits) > 1:
                n_mq_gates += 1

        return n_mq_gates

    @property
    def symbolic_params(self):
        """
        Returns a set of symbolic parameters used in the circuit in the chronological
        order.

        Returns:
            list: list of all the sympy symbols used as params of gates in the circuit.
        """
        symbolic_params = []
        for gate in self.gates:
            symbolic_params_per_gate = gate.symbolic_params
            for param in symbolic_params_per_gate:
                if param not in symbolic_params:
                    symbolic_params.append(param)

        return symbolic_params

    def __eq__(self, anotherCircuit):
        """Comparison between two Circuit objects."""
        if self.name != anotherCircuit.name:
            return False
        if len(self.qubits) != len(anotherCircuit.qubits):
            return False
        for i in range(len(self.qubits)):
            if str(self.qubits[i]) != str(anotherCircuit.qubits[i]):
                return False

        if len(self.gates) != len(anotherCircuit.gates):
            return False
        for i in range(len(self.gates)):
            if self.gates[i] != anotherCircuit.gates[i]:
                return False

        return True

    def __add__(self, other_circuit):
        """Add two circuits."""

        qubit_indices = set(
            [qubit.index for qubit in self.qubits]
            + [qubit.index for qubit in other_circuit.qubits]
        )

        new_circuit = Circuit()

        for qubit_index in qubit_indices:
            new_circuit.qubits.append(Qubit(qubit_index))

        new_circuit.gates = self.gates + other_circuit.gates

        return new_circuit

    def get_qubits(self):
        """Returns a list of qubit indices (ints)."""

        return [q.index for q in self.qubits]

    def evaluate(self, symbols_map):
        """
        Returns a copy of a circuit with specified symbolic parameters evaluated to
        provided values.

        Args:
            symbols_map list(tuple(sympy.Basic, number)): List containing symbols and
            values that they should take.
        """
        new_circuit = type(self)()
        new_circuit.name = self.name
        new_circuit.qubits = self.qubits
        new_circuit.info = self.info
        gates = []

        all_symbols_in_map = set([item[0] for item in symbols_map])
        if len(all_symbols_in_map - set(self.symbolic_params)) > 0:
            warnings.warn(
                """
                Trying to evaluate circuit with symbols not existing in the circuit:
                Symbols in circuit: {0}
                Symbols in the map: {1}
                """.format(
                    self.symbolic_params, all_symbols_in_map
                ),
                Warning,
            )

        for gate in self.gates:
            gates.append(gate.evaluate(symbols_map))

        new_circuit.gates = gates
        return new_circuit

    def to_pyquil(self):
        """Converts the circuit to a pyquil Program object."""

        output = Program()
        if self.gates is not None:
            for gate in self.gates:
                output = add_gate_to_pyquil_program(output, gate)
        return output

    def to_cirq(self, cirq_qubits=None):
        """Converts the circuit to a cirq Circuit object.
        NOTE: Here we always assume that the resulting circuit acts on a linear chain of
        qubits.

        Args:
            cirq_qubits: list[cirq.LineQubit]
                (optional) A list of cirq.LineQubit objects.
        """

        qubits = []
        if cirq_qubits is None:
            if self.qubits is not None:
                if self.info["label"] == "cirq":
                    for q in self.qubits:
                        qkey = q.info["QubitKey"]
                        if q.info["QubitType"] == "GridQubit":
                            qubits.append(cirq.GridQubit(qkey[0], qkey[1]))
                        if q.info["QubitType"] == "LineQubit":
                            qubits.append(cirq.LineQubit(qkey))
                else:
                    qubits = [cirq.LineQubit(i) for i in self.get_qubits()]
        else:
            if len(cirq_qubits) < len(self.qubits):
                raise Exception(
                    f"Input qubit register size is {len(cirq_qubits)}, which is not "
                    "enough to represent this Circuit object that acts on "
                    f"{len(self.qubits)} qubits"
                )
            qubits = cirq_qubits

        if self.gates is not None:
            gates = [g.to_cirq(cirq_qubits) for g in self.gates]
        else:
            gates = []

        cirq_circuit = cirq.Circuit()
        cirq_circuit.append(gates, strategy=cirq.circuits.InsertStrategy.EARLIEST)
        return cirq_circuit

    def to_qiskit(self):
        """Converts the circuit to a qiskit QuantumCircuit object."""
        qiskit_circuit = qiskit.QuantumCircuit()  # New qiskit circuit object
        qreg = None
        creg = None

        if (
            self.qubits is not None and self.qubits != []
        ):  # If there are qubits in the circuit, add them to the new qiskit circuit
            max_qindex = max([q.index for q in self.qubits])
            qreg = qiskit.QuantumRegister(max_qindex + 1, "q")
            creg = qiskit.ClassicalRegister(max_qindex + 1, "c")
            qiskit_circuit.add_register(qreg)
            qiskit_circuit.add_register(creg)

        if self.gates is not None:
            for gate in self.gates:
                qiskit_gate_data = gate.to_qiskit(
                    qreg, creg
                )  # provide the gate conversion with the associated QuantumRegister

                # total number of entries in the list (which is 3x the number of
                # elementary gates)
                N = len(
                    qiskit_gate_data
                )

                if N % 3 != 0:
                    raise ValueError(
                        f"The number of entries in qiskit_gate_data is {N} which is not"
                        " a multiple of 3"
                    )
                for index in np.linspace(0, N - 3, N // 3):
                    qiskit_circuit.append(
                        qiskit_gate_data[int(index)],
                        qargs=qiskit_gate_data[int(index) + 1],
                        cargs=qiskit_gate_data[int(index) + 2],
                    )

        return qiskit_circuit

    def to_dict(self, serialize_gate_params=True):
        """Creates a dictionary representing a circuit.

        Args:
            serialize_gate_params(bool): if true, it will change gate params from sympy
            to strings (if applicable).

        Returns:
            dictionary (dict): the dictionary
        """

        if self.gates is not None:
            gates_entry = [
                gate.to_dict(serialize_params=serialize_gate_params)
                for gate in self.gates
            ]
        else:
            gates_entry = None

        if self.qubits is not None:
            qubits_entry = [qubit.to_dict() for qubit in self.qubits]
        else:
            qubits_entry = None

        dictionary = {
            "schema": SCHEMA_VERSION + "-circuit",
            "name": self.name,
            "gates": gates_entry,
            "qubits": qubits_entry,
            "info": self.info,
        }

        return dictionary

    def to_unitary(self):
        """Creates a unitary matrix representing the circuit.

        Returns:
            An array representing the unitary matrix.
        """

        return self.to_cirq()._unitary_()

    def to_text_diagram(self, transpose=False):
        """Gets a text diagram representing the circuit.

        transpose (bool): if true, arrange qubit wires vertically instead of
            horizontally.

        Returns:
            str: a string containing the text diagram
        """

        return self.to_cirq().to_text_diagram(transpose=transpose)

    def to_quil(self):
        """Gets the quil program representing the circuit.
        Returns:
            str: a string containing the quil program
        """
        return self.to_pyquil().out()

    def to_qpic(self):
        """Generates a string that can be used by qpic to build a picture of the
        circuit.

        Returns:
            str: a qpic string
        """

        qpic_string = ""

        for qubit in sorted(self.qubits, key=lambda q: q.index):
            qpic_string += "w{} W {}\n".format(qubit.index, qubit.index)

        for gate in self.gates:
            qpic_string += gate.to_qpic() + "\n"

        return qpic_string

    def __str__(self):
        """Get a string representation of the circuit.
        Returns:
            str: a string representation of the circuit
        """
        return self.to_text_diagram()

    @classmethod
    def from_dict(cls, dictionary):
        """Loads information of the circuit from a dictionary. This corresponds to the
        serialization routines to_dict for Circuit, Gate and Qubit.

        Args:
            dictionary (dict): the dictionary

        Returns:
            A core.circuit.Circuit object
        """

        output = cls(name=dictionary["name"])
        if dictionary["gates"] is not None:
            output.gates = [Gate.from_dict(gate) for gate in dictionary["gates"]]
        else:
            output.gates = None

        if dictionary["qubits"] is not None:
            output.qubits = [Qubit.from_dict(qubit) for qubit in dictionary["qubits"]]
        else:
            output.qubits = None
        output.info = dictionary["info"]
        return output

    def from_pyquil(self, pyquil_circuit):
        """Converts a pyquil Program object to a core.Circuit object.

        Args:
            pyquil_circuit: Program object(pyquil)
            name: string
                Name of the converted core.Circuit object.

        """

        self.info["label"] = "pyquil"

        _gatelist = []
        _qubits = []

        if len(pyquil_circuit) == 0:
            return

        _pyquil_qubits = []  # list of currently found *pyquil* qubits
        for gate in pyquil_circuit:

            _gatequbits = []
            for qubit in gate.qubits:

                def qubit_in_list(
                    qubit, qubitlist
                ):  # check if a pyquil qubit is in a list of pyquil qubits
                    output = False
                    out_index = []
                    for q in qubitlist:
                        if qubit.index == q.index:
                            output = True
                            out_index = q.index
                            break
                    return output, out_index

                _flag, _index = qubit_in_list(qubit, _pyquil_qubits)
                if _flag is False:
                    _pyquil_qubits.append(qubit)
                    _new_Qubit = Qubit.from_pyquil(qubit)
                    _qubits.append(_new_Qubit)
                    _gatequbits.append(_new_Qubit)
                else:
                    for q in _qubits:
                        if q.index == _index:
                            _old_Qubit = q
                            break
                    _gatequbits.append(_old_Qubit)

            _gatelist.append(Gate.from_pyquil(gate, _gatequbits))

        self.gates = _gatelist
        self.qubits = _qubits

    def from_cirq(self, cirq_circuit):
        """Convert from a cirq Circuit object to a core.Circuit object.

        Args:
            cirq_circuit: cirq Cirquit object.
                See the following: https://github.com/quantumlib/Cirq

        """
        self.info["label"] = "cirq"

        _gatelist = []
        _qubits = []

        if (
            len(cirq_circuit) == 0
            or sum([len(m.operations) for m in cirq_circuit]) == 0
        ):
            return

        _cirq_qubits = []  # list of currently found *cirq* qubits
        for moment in cirq_circuit:
            for op in moment.operations:
                _gatequbits = []
                for qubit in op.qubits:

                    def qubit_in_list(
                        qubit, qubitlist
                    ):  # check if a cirq qubit is in a list of cirq qubits
                        # if yes return the index
                        output = False
                        out_index = []
                        for q in qubitlist:
                            if isinstance(qubit, cirq.GridQubit) and isinstance(
                                q, cirq.GridQubit
                            ):
                                if qubit.row == q.row and qubit.col == q.col:
                                    output = True
                                    out_index = (q.row, q.col)
                                    break
                            elif isinstance(qubit, cirq.LineQubit) and isinstance(
                                qubit, cirq.LineQubit
                            ):
                                if qubit.x == q.x:
                                    output = True
                                    out_index = q.x
                                    break
                            else:
                                raise TypeError(
                                    "(Cirq) Qubit and Qubit list elements not of the "
                                    "same kind."
                                )
                        return output, out_index

                    _flag, _index = qubit_in_list(qubit, _cirq_qubits)
                    if _flag is False:  # if the qubit is not seen before
                        _cirq_qubits.append(
                            qubit
                        )  # add the cirq qubit to the list of cirq qubits seen
                        _new_Qubit = Qubit.from_cirq(
                            qubit, qubit.x
                        )  # generate a new qubit
                        _qubits.append(_new_Qubit)
                        _gatequbits.append(_new_Qubit)
                    else:  # if the qubit is already seen before
                        for (
                            q
                        ) in (
                            _qubits
                        ):  # search for the old Qubit object in the _qubits list
                            if q.info["QubitKey"] == _index:
                                _old_Qubit = q
                                break
                        _gatequbits.append(_old_Qubit)
                _gatelist.append(Gate.from_cirq(op, _gatequbits))

        self.gates = _gatelist
        self.qubits = _qubits

    def from_qiskit(self, qiskit_circuit):
        """Convert from a qiskit QuantumCircuit object to a core.circuit.Circuit object.

        Args:
            qiskit_circuit: qiskit QuantumCircuit object.

        """

        self.name = qiskit_circuit.name
        self.info["label"] = "qiskit"

        _gatelist = []  # list of gates for the output Circuit object
        _qubits = []  # list of qubits for the output Circuit object

        if len(qiskit_circuit.data) == 0:
            return

        _qiskit_qubits = []  # list of qiskit qubits in the circuit object
        for gate_data in qiskit_circuit.data:
            _gatequbits = []
            for qubit in gate_data[1]:

                def qubit_in_list(
                    qubit, qubitlist
                ):  # check if a qiskit qubit is in a list of qiskit qubit
                    output = False
                    for q in qubitlist:
                        if qubit == q:
                            output = True
                            break
                    return output

                if (
                    qubit_in_list(qubit, _qiskit_qubits) == 0
                ):  # if the qubit is not seen before
                    _qiskit_qubits.append(
                        qubit
                    )  # add the qiskit qubit to the list of seen qiskit qubits
                    _new_Qubit = Qubit.from_qiskit(
                        qubit, qubit.index
                    )  # generate a new Qubit object
                    _qubits.append(
                        _new_Qubit
                    )  # add to the list of Qubit objects for the output Circuit object
                    _gatequbits.append(
                        _new_Qubit
                    )  # add to the list of Qubit objects that the gate acts on
                else:  # if the qubit is already seen before
                    for (
                        q
                    ) in _qubits:  # search for the old Qubit object in the _qubits list
                        if q.info["num"] == qubit.index:
                            _old_Qubit = q
                            break
                    _gatequbits.append(_old_Qubit)

            zap_gate = Gate.from_qiskit(gate_data[0], _gatequbits)
            if zap_gate is not None:
                _gatelist.append(zap_gate)

        self.gates = _gatelist
        self.qubits = _qubits

def save_circuit(circuit, file_or_path):
    """Saves a circuit object to a file.

    Args:
        circuit: the circuit to be saved
        file_or_path: the name of the file
    """
    payload = circuit.to_dict(serialize_gate_params=True)
    try:
        json.dump(payload, file_or_path)
    except AttributeError:
        with open(file_or_path, "w") as f:
            json.dump(payload, f)

def load_circuit(file):
    """Loads a circuit from a file.

    Args:
        file (str or file-like object): the name of the file, or a file-like object.

    Returns:
        circuit (core.Circuit): the circuit
    """

    if isinstance(file, str):
        with open(file, "r") as f:
            data = json.load(f)
    else:
        data = json.load(file)

    return Circuit.from_dict(data)

def save_circuit_set(circuit_set, filename):
    """Save a circuit set to a file.

    Args:
        circuit_set (list): a list of core.Circuit objects
        file (str or file-like object): the name of the file, or a file-like object
    """
    dictionary = {}
    dictionary["schema"] = SCHEMA_VERSION + "-circuit_set"
    dictionary["circuits"] = []
    for circuit in circuit_set:
        dictionary["circuits"].append(circuit.to_dict(serialize_gate_params=True))
    with open(filename, "w") as f:
        f.write(json.dumps(dictionary, indent=2))

def load_circuit_set(file):
    """Load a set of circuits from a file.

    Args:
        file (str or file-like object): the name of the file, or a file-like object.

    Returns:
        circuit_set (list): a list of core.Circuit objects
    """
    if isinstance(file, str):
        with open(file, "r") as f:
            data = json.load(f)
    else:
        data = json.load(file)

    circuit_set = []
    for circuit_dict in data["circuits"]:
        circuit_set.append(Circuit.from_dict(circuit_dict))
    return circuit_set

def pyquil2cirq(qprog):
    """Convert a pyquil Program to a cirq Circuit.

    Currently supports only common single- and two-qubit gates.

    Args:
        qprog (pyquil.quil.Program): the program to be converted.

    Returns:
        circuit (cirq.Cirquit): the converted circuit"""

    # A map between gate names used by pyquil and cirq gate objects
    op_map = {
        "X": cirq.X,
        "Y": cirq.Y,
        "Z": cirq.Z,
        "T": cirq.T,
        "H": cirq.H,
        "S": cirq.S,
        "RX": cirq.XPowGate,
        "RY": cirq.YPowGate,
        "RZ": cirq.ZPowGate,
        "CNOT": cirq.CNOT,
        "SWAP": cirq.SWAP,
        "CZ": cirq.CZ,
        "CPHASE": cirq.ops.common_gates.CZPowGate,
    }

    # Create the qubits. The row of each grid qubit is equal to the index
    # of the corresponding pyquil qubit.
    qubits = [cirq.GridQubit(i, 0) for i in qprog.get_qubits()]

    # A map between the row of the qubit and the index in the qubits array
    qubit_map = {}
    for i in range(len(qubits)):
        qubit_map[qubits[i].row] = i

    circuit = cirq.Circuit()

    for gate in qprog:
        if not op_map.get(gate.name):
            raise ValueError("Gate {} not yet supported".format(gate.name))

        # Find the cirq qubits that this gate acts on
        target_qubits = [qubits[qubit_map[q.index]] for q in gate.qubits]

        # Create the cirq gate
        if len(gate.params) == 0:
            cirq_gate = op_map[gate.name](*target_qubits)
        elif len(gate.params) == 1:
            cirq_gate = op_map[gate.name](exponent=gate.params[0] / np.pi)(
                *target_qubits
            )
        else:
            raise ValueError(
                "Gates with more than one parameter not yet supported: {}".format(gate)
            )

        # Append the gate to the circuit
        circuit.append(cirq_gate, strategy=cirq.circuits.InsertStrategy.EARLIEST)

    return circuit

def cirq2pyquil(circuit):
    """Convert a cirq Circuit to a pyquil Program.

    Currently supports only common single- and two-qubit gates.

    Args:
        circuit (cirq.Cirquit): the converted circuit.

    Returns:
        qprog (pyquil.quil.Program): the program to be converted."""

    # A map between cirq gate string representations and pyquil gate classes
    op_repr_map = {
        "cirq.X": pyquil.gates.X,
        "cirq.Y": pyquil.gates.Y,
        "cirq.Z": pyquil.gates.Z,
        "cirq.T": pyquil.gates.T,
        "cirq.H": pyquil.gates.H,
        "cirq.S": pyquil.gates.S,
        "cirq.CNOT": pyquil.gates.CNOT,
        "cirq.SWAP": pyquil.gates.SWAP,
        "cirq.CZ": pyquil.gates.CZ,
    }

    # A map between cirq gate classes and pyquil gate classes. Perhaps better to parse
    # repr?
    op_type_map = {
        cirq.ops.common_gates.XPowGate: pyquil.gates.RX,
        cirq.ops.common_gates.YPowGate: pyquil.gates.RY,
        cirq.ops.common_gates.ZPowGate: pyquil.gates.RZ,
        cirq.ops.common_gates.CZPowGate: pyquil.gates.CPHASE,
    }

    # Create a map from row/column tuples to linear qubit index
    qubit_map = {}
    qubit_count = 0
    qubit = next(iter(circuit.all_qubits()))  # Grab a random qubit
    if isinstance(qubit, cirq.GridQubit):
        def qubit_key(q):
            return (q.row, q.col)
    elif isinstance(qubit, cirq.LineQubit):
        def qubit_key(q):
            return q.x
    else:
        raise ValueError("Qubit type {} not yet supported".format(type(qubit)))
    for qubit in sorted(circuit.all_qubits(), key=qubit_key):
        qubit_map[qubit_key(qubit)] = qubit_count
        qubit_count += 1

    # Create the program
    qprog = pyquil.quil.Program()

    def add_to_program(op):
        """Add a cirq op to the pyquil program qprog."""

        # Find the linear indices of the qubits acted on by this operation
        qubits = [qubit_map[qubit_key(q)] for q in op.qubits]

        # First check if the string representation matches known gates
        if op_repr_map.get(repr(op.gate)):
            qprog.inst(op_repr_map[repr(op.gate)](*qubits))

        # Next check if the type of the gate object matches known gates
        elif op_type_map.get(type(op.gate)):
            rads = op.gate.exponent * np.pi
            pyquil_gate = op_type_map[type(op.gate)]
            qprog.inst(pyquil_gate(rads, *qubits))

        # Decompose if PhasedXPowGate or HPowGate
        elif isinstance(op.gate, cirq.PhasedXPowGate) or isinstance(
            op.gate, cirq.HPowGate
        ):
            ops = cirq.decompose(op)
            for op in ops:
                add_to_program(op)

        elif isinstance(op.gate, cirq.XXPowGate):
            q1, q2 = op.qubits
            ops = [
                cirq.H(q1),
                cirq.H(q2),
                cirq.CNOT(q1, q2),
                cirq.rz(op.gate.exponent * pi)(q2),
                cirq.CNOT(q1, q2),
                cirq.H(q1),
                cirq.H(q2),
            ]
            for op in ops:
                add_to_program(op)

        elif isinstance(op.gate, cirq.YYPowGate):
            q1, q2 = op.qubits
            ops = [
                cirq.Z(q1) ** 0.5,
                cirq.Z(q2) ** 0.5,
                cirq.H(q1),
                cirq.H(q2),
                cirq.CNOT(q1, q2),
                cirq.rz(op.gate.exponent * pi)(q2),
                cirq.CNOT(q1, q2),
                cirq.H(q1),
                cirq.H(q2),
                cirq.Z(q1) ** -0.5,
                cirq.Z(q2) ** -0.5,
            ]
            for op in ops:
                add_to_program(op)

        elif isinstance(op.gate, cirq.ZZPowGate):
            q1, q2 = op.qubits
            ops = [
                cirq.CNOT(q1, q2),
                cirq.rz(op.gate.exponent * pi)(q2),
                cirq.CNOT(q1, q2),
            ]
            for op in ops:
                add_to_program(op)

        else:
            raise ValueError("Gate {} not yet supported".format(op.gate))

    for moment in circuit:
        for op in moment.operations:
            add_to_program(op)

    return qprog

def add_gate_to_pyquil_program(pyquil_program, gate):
    """Add the definition of a gate to a pyquil Program object if the gate is
    not currently defined.

    Args:
        pyquil_program: pyquil.Program
            The input Program object to which the gate is going to be added.
        gate: Gate (core.circuit)
            The Gate object describing the gate to be added.

    Returns:
        A new pyquil.Program object with the definition of the new gate being added.
    """

    if gate.name in COMMON_GATES:  # if a gate is already included in pyquil
        return pyquil_program + gate.to_pyquil()  # do nothing
    elif gate.name in UNIQUE_GATES:  # if a gate is unique to a specific package
        if gate.name == "ZXZ":
            beta = pyquil.quilatom.Parameter("beta")
            gamma = pyquil.quilatom.Parameter("gamma")
            zxz_unitary = np.array(
                [
                    [
                        quil_cos(gamma / 2),
                        -quil_sin(beta) * quil_sin(gamma / 2)
                        - 1j * quil_cos(beta) * quil_sin(gamma / 2),
                    ],
                    [
                        quil_sin(beta) * quil_sin(gamma / 2)
                        - 1j * quil_cos(beta) * quil_sin(gamma / 2),
                        quil_cos(gamma / 2),
                    ],
                ]
            )
            zxz_def = pyquil.quilbase.DefGate("ZXZ", zxz_unitary, [beta, gamma])
            ZXZ = zxz_def.get_constructor()
            return (
                pyquil_program
                + zxz_def
                + ZXZ(gate.params[0], gate.params[1])(gate.qubits[0].index)
            )
        if gate.name == "RH":
            beta = pyquil.quilatom.Parameter("beta")
            phase_factor = quil_cos(beta / 2) + 1j * quil_sin(beta / 2)
            elem00 = quil_cos(beta / 2) - 1j * 1 / np.sqrt(2) * quil_sin(beta / 2)
            elem01 = -1j * 1 / np.sqrt(2) * quil_sin(beta / 2)
            elem10 = -1j * 1 / np.sqrt(2) * quil_sin(beta / 2)
            elem11 = quil_cos(beta / 2) + 1j * 1 / np.sqrt(2) * quil_sin(beta / 2)
            rh_unitary = np.array(
                [
                    [phase_factor * elem00, phase_factor * elem01],
                    [phase_factor * elem10, phase_factor * elem11],
                ]
            )
            rh_def = pyquil.quilbase.DefGate("RH", rh_unitary, [beta])
            RH = rh_def.get_constructor()
            return pyquil_program + rh_def + RH(gate.params[0])(gate.qubits[0].index)
        if gate.name == "XX":
            # Reference for XX implementation in cirq:
            # https://github.com/quantumlib/Cirq/blob/a61e51b53612735e93b3bb8a7605030c499cd6c7/cirq/ops/parity_gates.py#L30
            # Reference for XX implementation in qiskit:
            # https://qiskit.org/documentation/stubs/qiskit.circuit.library.RXXGate.html
            beta = pyquil.quilatom.Parameter("beta")
            elem_cos = quil_cos(beta)
            elem_sin = -1j * quil_sin(beta)
            xx_unitary = np.array(
                [
                    [elem_cos, 0, 0, elem_sin],
                    [0, elem_cos, elem_sin, 0],
                    [0, elem_sin, elem_cos, 0],
                    [elem_sin, 0, 0, elem_cos],
                ]
            )
            xx_def = pyquil.quilbase.DefGate("XX", xx_unitary, [beta])
            XX = xx_def.get_constructor()
            return (
                pyquil_program
                + xx_def
                + XX(gate.params[0])(gate.qubits[0].index, gate.qubits[1].index)
            )
        if gate.name == "YY":
            # Reference for YY implementation in cirq:
            # https://github.com/quantumlib/Cirq/blob/a61e51b53612735e93b3bb8a7605030c499cd6c7/cirq/ops/parity_gates.py#L142
            # Reference for YY implementation in qiskit:
            # https://qiskit.org/documentation/stubs/qiskit.circuit.library.RYYGate.html
            beta = pyquil.quilatom.Parameter("beta")
            elem_cos = quil_cos(beta)
            elem_sin = 1j * quil_sin(beta)
            yy_unitary = np.array(
                [
                    [elem_cos, 0, 0, elem_sin],
                    [0, elem_cos, -elem_sin, 0],
                    [0, -elem_sin, elem_cos, 0],
                    [elem_sin, 0, 0, elem_cos],
                ]
            )
            yy_def = pyquil.quilbase.DefGate("YY", yy_unitary, [beta])
            YY = yy_def.get_constructor()
            return (
                pyquil_program
                + yy_def
                + YY(gate.params[0])(gate.qubits[0].index, gate.qubits[1].index)
            )
        if gate.name == "ZZ":
            # Reference for ZZ implementation in cirq:
            # https://github.com/quantumlib/Cirq/blob/a61e51b53612735e93b3bb8a7605030c499cd6c7/cirq/ops/parity_gates.py#L254
            # Reference for ZZ implementation in qiskit:
            # https://qiskit.org/documentation/stubs/qiskit.circuit.library.RYYGate.html
            beta = pyquil.quilatom.Parameter("beta")
            elem_cos = quil_cos(beta)
            elem_sin = 1j * quil_sin(beta)
            zz_unitary = np.array(
                [
                    [elem_cos - elem_sin, 0, 0, 0],
                    [0, elem_cos + elem_sin, 0, 0],
                    [0, 0, elem_cos + elem_sin, 0],
                    [0, 0, 0, elem_cos - elem_sin],
                ]
            )
            zz_def = pyquil.quilbase.DefGate("ZZ", zz_unitary, [beta])
            ZZ = zz_def.get_constructor()
            return (
                pyquil_program
                + zz_def
                + ZZ(gate.params[0])(gate.qubits[0].index, gate.qubits[1].index)
            )
        if gate.name == "XY":
            return pyquil_program + gate.to_pyquil()  # do nothing

        if gate.name == "U1ex":  # IBM U1ex gate (arXiv:1805.04340v1)
            alpha = pyquil.quilatom.Parameter("alpha")
            beta = pyquil.quilatom.Parameter("beta")
            elem_cos = quil_cos(beta)
            elem_sin = 1j * quil_sin(beta)
            unitary = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]
            unitary[1][1] = quil_cos(alpha)
            unitary[2][2] = -quil_cos(alpha)
            unitary[2][1] = (quil_cos(beta) - 1j * quil_sin(beta)) * quil_sin(alpha)
            unitary[1][2] = (quil_cos(beta) + 1j * quil_sin(beta)) * quil_sin(alpha)
            u1ex_def = pyquil.quilbase.DefGate("U1ex", np.array(unitary), [alpha, beta])
            U1ex = u1ex_def.get_constructor()
            output_program = pyquil_program + U1ex(gate.params[0], gate.params[1])(
                gate.qubits[0].index, gate.qubits[1].index
            )
            gate_already_defined = False
            for gate_definition in pyquil_program.defined_gates:
                if gate_definition.name == "U1ex":
                    gate_already_defined = True
                    break
            if not gate_already_defined:
                output_program = output_program + u1ex_def
            return output_program
        if gate.name == "U2ex":  # IBM U2ex gate (arXiv:1805.04340v1)
            alpha = pyquil.quilatom.Parameter("alpha")
            unitary = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]
            unitary[1][1] = quil_cos(2 * alpha)
            unitary[2][2] = quil_cos(2 * alpha)
            unitary[2][1] = -1j * quil_sin(2 * alpha)
            unitary[1][2] = -1j * quil_sin(2 * alpha)
            u2ex_def = pyquil.quilbase.DefGate("U2ex", np.array(unitary), [alpha])
            U2ex = u2ex_def.get_constructor()
            output_program = pyquil_program + U2ex(gate.params[0])(
                gate.qubits[0].index, gate.qubits[1].index
            )
            gate_already_defined = False
            for gate_definition in pyquil_program.defined_gates:
                if gate_definition.name == "U2ex":
                    gate_already_defined = True
                    break
            if not gate_already_defined:
                output_program = output_program + u1ex_def
            return output_program
        if gate.name == "MEASURE":
            reg_name = "r" + str(gate.qubits[0].index)

            ro = pyquil_program.declare(reg_name, "BIT", 1)
            return pyquil_program + MEASURE(gate.qubits[0].index, ro[0])
        if gate.name == "BARRIER":
            return pyquil_program

def add_ancilla_register_to_circuit(circuit, n_qubits_ancilla_register):
    """Add a register of ancilla qubits (qubit + identity gate) to an existing circuit.

    Args:
        circuit (core.Circuit): circuit to be extended
        n_qubits_ancilla_register (int): number of ancilla qubits to add
    Returns:
        core.Circuit: extended circuit

    """
    extended_circuit = Circuit()
    n_qubits = len(circuit.qubits)
    pyquil_circuit = circuit.to_pyquil()
    for i in range(n_qubits_ancilla_register):
        pyquil_circuit += I(n_qubits + i)
    extended_circuit.from_pyquil(pyquil_circuit)
    return extended_circuit


import re

from ..utils.web import download_website, download_m3u8_formats
from ..utils.parser import parse_json
from ..utils.youtube import get_yt_initial_data, get_yt_player_config
from ..utils.other import try_get, get_format_from_data, get_highest_thumbnail
from ..log import warning

already_checked_video_ids = []

def is_live_sponsor_only_streams(channel_class, SharedVariables):
    """

    Checks for unlisted youtube live stream links in sponsor only community tab.
    This is to record "sponsor only" streams. Really, it's just a unlisted stream.

    :type channel_class: ChannelInfo
    :type SharedVariables: Variables shared around Processes.
    """

    def loadVideoData():
        """

        This is used to grab video info from the Youtube site, like video_id, to check if already live,
        and the stream url if already live.
        Everything else would use heartbeat and get video info url.

        :return: Nothing. It edits the class.
        """
        website_string = download_website('https://www.youtube.com/watch?v={0}'.format(video_id),
                                          SharedVariables=SharedVariables)
        if website_string is None:
            return [False, "Failed getting Youtube Data from the internet! "
                           "This means there is no good internet available!"]
        if website_string == 404:
            return [False, "Failed getting Youtube Data! \"{0}\" doesn't exist as a channel id!".format(
                channel_class.channel_id)]
        yt_player_config = try_get(get_yt_player_config(website_string), lambda x: x['args'], dict)
        player_response = parse_json(try_get(yt_player_config, lambda x: x['player_response'], str))
        videoDetails = try_get(player_response, lambda x: x['videoDetails'], dict)
        if yt_player_config and videoDetails:
            if "isLiveContent" in videoDetails and \
                    videoDetails['isLiveContent'] and \
                    ("isLive" in videoDetails or "isUpcoming" in videoDetails):
                channel_class.video_id = try_get(videoDetails, lambda x: x['videoId'], str)
            else:
                return [False, "Found a stream, the stream seemed to be a non-live stream."]
        else:
            return [False, "Unable to get yt player config, and videoDetails."]

        # TO AVOID REPEATING REQUESTS.
        if player_response:
            # playabilityStatus is legit heartbeat all over again..
            playabilityStatus = try_get(player_response, lambda x: x['playabilityStatus'], dict)
            status = try_get(playabilityStatus, lambda x: x['status'], str)
            reason = try_get(playabilityStatus, lambda x: x['reason'], str)
            if playabilityStatus and status:
                if 'OK' in status:
                    if reason and 'ended' in reason:
                        return [False, reason]
                    if "streamingData" not in player_response:
                        return [False, "No StreamingData, Youtube bugged out!"]
                    manifest_url = str(try_get(player_response, lambda x: x['streamingData']['hlsManifestUrl'], str))
                    if not manifest_url:
                        return [False, "Unable to find Manifest URL."]
                    formats = download_m3u8_formats(manifest_url)
                    if formats is None or len(formats) is 0:
                        return [False, "There were no formats found! Even when the streamer is live."]
                    f = get_format_from_data(formats, None)
                    thumbnails = try_get(videoDetails, lambda x: x['thumbnail']['thumbnails'], list)
                    if thumbnails:
                        channel_class.thumbnail_url = get_highest_thumbnail(thumbnails)
                    channel_class.YoutubeStream = {
                        'stream_resolution': '{0}x{1}'.format(str(f['width']), str(f['height'])),
                        'HLSStreamURL': f['url'],
                        'title': try_get(videoDetails, lambda x: x['title'], str),
                        'description': videoDetails['shortDescription'],
                    }
                    channel_class.video_id = try_get(videoDetails, lambda x: x['videoId'], str)

        return [True, "OK"]

    CommunityPosts = readCommunityPosts(channel_class, SharedVariables=SharedVariables)
    if CommunityPosts:
        for communityTabMessage in CommunityPosts:
            dict_urls = communityTabMessage['contentText']['URLs']
            # FIND ANY VIDEO ID IN MESSAGE
            if dict_urls:
                for url in dict_urls:
                    video_id_object = re.search(r'youtu.be\/(.+)|youtube.com\/watch\?v=(.+)', url)
                    if video_id_object:
                        video_id_tuple = video_id_object.groups()
                        video_id = next(x for x in video_id_tuple if x is not None)
                        if video_id:
                            if video_id not in already_checked_video_ids:
                                already_checked_video_ids.append(video_id)
                                ok, message = loadVideoData()
                                if not ok:
                                    warning("Failed stream, {0}. {1}".format(video_id, message))
                                if ok:
                                    return True
    elif CommunityPosts is None:
        return None
    return False

def readCommunityPosts(channel_class, SharedVariables=None):
    def getCommunityTabInfo(tabList):
        """

        Gets Community Tab information from a list of all the Youtube Channel Tabs.
        For Example, Youtube Channel featured tab.

        :type tabList: list
        """
        for tab in tabList:
            tab = try_get(tab, lambda x: x['tabRenderer'], dict)
            if tab:
                title = try_get(tab, lambda x: x['title'], str)
                if title is not None and 'Community' in title:
                    return tab
        return None

    def getCommunityTabListMessages(communityTabSectionRenderer):
        """

        Simplifies a list of all Community Tab Messages information to a simple dict.

        :type communityTabSectionRenderer: list
        """

        def getMessage(communityTabMessageInfo):
            """

            Gets full string message from backstagePostRenderer (Message Holder for community Messages).

            :type communityTabMessageInfo: dict
            """
            communityMessage = []
            communityURL = []

            textHolder = try_get(communityTabMessageInfo, lambda x: x['contentText'], dict)
            if textHolder:
                if 'simpleText' in textHolder:
                    communityMessage.append(try_get(textHolder, lambda x: x['simpleText'], str))
                else:
                    textListHolder = try_get(textHolder, lambda x: x['runs'], list)
                    if textListHolder:
                        for textHolder in textListHolder:
                            if 'navigationEndpoint' in textHolder:
                                # Due to Youtube simplifying URLS. This is used to grab all of the url.
                                fullUrl = try_get(textHolder, lambda x: x['navigationEndpoint'][
                                    'urlEndpoint']['url'], str)
                                communityMessage.append(fullUrl)
                                if fullUrl:
                                    communityURL.append(fullUrl)
                            else:
                                partMessage = try_get(textHolder, lambda x: x['text'], str)
                                if partMessage:
                                    communityMessage.append(partMessage)
            community = {
                'communityMessage': ''.join(communityMessage),
                'URLs': communityURL
            }
            return community

        messages = []

        for communityMessageInfo in communityTabSectionRenderer:
            communityMessageInfo = try_get(communityMessageInfo, lambda x: x['backstagePostThreadRenderer'][
                'post']['backstagePostRenderer'], dict)
            if communityMessageInfo:
                message = {
                    'postID': try_get(communityMessageInfo, lambda x: x['postId'], str),
                    'authorText': try_get(communityMessageInfo, lambda x: x['authorText']['simpleText'], str),
                    'contentText': getMessage(communityMessageInfo),
                }
                if message['contentText'] is not None:
                    messages.append(message)

        return None if len(messages) == 0 else messages

    headers = {"DNT": 1, "upgrade-insecure-requests": 1}
    url = 'https://www.youtube.com/channel/{0}/community'.format(channel_class.channel_id)
    website = download_website(
        url,
        headers=headers, SharedVariables=SharedVariables)
    if type(website) is bool or website is None:
        return None

    youtubeInitialData = get_yt_initial_data(website)
    if youtubeInitialData is None:
        warning("Unable to find Initial Data.")
        return False
    twoColumnBrowseResultsRenderer = try_get(youtubeInitialData, lambda x: x['contents'][
        'twoColumnBrowseResultsRenderer'], dict)
    tabs = try_get(twoColumnBrowseResultsRenderer, lambda x: x['tabs'], list)
    communityTab = getCommunityTabInfo(tabs)
    itemSectionRenderer = try_get(communityTab, lambda x: x['content']['sectionListRenderer']['contents'][
        0]['itemSectionRenderer']['contents'], list)
    communityTabMessages = None
    if itemSectionRenderer:
        communityTabMessages = getCommunityTabListMessages(itemSectionRenderer)
    return communityTabMessages


import json
import sys
from matplotlib import pyplot as plt
import bs4
import requests
from bs4 import BeautifulSoup
import time
import os
from datetime import datetime

holds = []
hold_symbols = set([])
filename = ""
start_time = datetime.now().strftime("%m-%d-%y-%H:%M:%S")

# This basically will check the status of all options for some symbol
# then loop over every target we have and make sure it doesn't match
# one of ours
# Should probably have it take in symbol and strike price and check one target status at a time
def check_target_status(symbol):

    url = "https://finance.yahoo.com/quote/" + symbol + "/options?p=" + symbol + "&date=1594944000"
    r = requests.get(url)

    soup = bs4.BeautifulSoup(r.text,"lxml")
    found_change = False

    call_table = soup.find('table',{'class':'calls W(100%) Pos(r) Bd(0) Pt(0) list-options'})
    call_row = call_table.find_all('tr')

    call_dates = call_table.find_all('td', {'class': 'data-col1'})
    call_strikes = call_table.find_all('td', {'class': 'data-col2'})
    call_prices = call_table.find_all('td', {'class': 'data-col3'})

    for i in range(0, len(call_dates)):

        date = call_dates[i].text
        strike = call_strikes[i].text
        price = call_prices[i].text

        for hold in holds:
            #targ_json = json.loads(targ)
            hold_strike = hold['strike']
            hold_symbol = hold['symbol']

            if (hold_strike == strike and symbol == hold_symbol):

                current_price_fl = round(float(price), 2)

                sp_list = []
                sp = hold['sell_price']

                if isinstance(sp, float):
                    sp_list.append(sp)
                else:
                    sp_list = list(sp)

                print("found hold w current price: " + str(current_price_fl) + ", but sell prices: " + str(sp_list))

                if (current_price_fl in sp_list):
                    # write to bought file to keep track of
                    print("AYOO would sell this hold option now: " + str(hold))

                    sell_opt = {
                        'symbol':symbol,
                        'strike': strike,
                        'bought_price': current_price_fl,
                        'sell_price': hold['sell_price'],
                    }

                    trigger_order(sell_opt, hold)
                else:
                    print("nope waiting on this hold sell price still: " + str(hold))

# Make a dictionary of each option where the keys are
# 'SYMBOL-STRIKE_PRICE' and the value is the list of prices
# it was throughout the time we were logging the option price
#

def form_bought_list(fpath):
    #print(bought_filepath)
    with open(fpath, "r") as a_file:

        for line in a_file:

            stripped_line = line.strip()

            if "filled order for" in stripped_line:
                #print("some important line: " + stripped_line)

                parts = stripped_line.split(">> ")
                bopt_str = parts[1]

                bopt = json.loads(bopt_str)
                bopt_symb = bopt['symbol']

                print("bopt as json: " + str(bopt))
                print("bopt symbol: " + str(bopt_symb))

                hold_symbols.add(bopt_symb)
                holds.append(bopt)

def write_to_sold(sopt):

    sells = "\nfake sold order for: " + str(json.dumps(sopt))
    sells += "\n"

    now = datetime.now()

    print("fed in target file was: " + filename)
    # get the date piece from the passed in scan data file
    # so we know when the targets were taken from
    #
    fileparts = filename.split("/")
    file_end = fileparts[2]
    chart_date = file_end.split("_")[2]

    day_string = now.strftime("%m-%d-%y")

    os.system("mkdir -p holds/" + day_string)
    fpath = "sells/" + day_string + "/sold_list_st-" + start_time + "_tl-" + chart_date + ".txt"

    print("writing out sold list to: " + fpath)

    f = open("sells/sold_list.txt", "a")
    f.write(sells)
    f.close()

def trigger_order(sopt, hold):

    print("triggering order to sell this hold now: " + str(sopt))

    cmd = "cd ibs && python3 sell.py " + sopt['symbol'] + " " + sopt['strike'] + " C"
    os.system(cmd)

    write_to_sold(sopt)
    holds.remove(hold)

def main(fpath):

    print("parsing the bought list file: " + fpath)
    form_bought_list(fpath)

    print("all me holds after parse: " + str(holds))

    write_to_sold({})

    while False:#len(holds) > 0:

        print("now would keep checking this: " + str(holds))
        print("by checking option status for: " + str(hold_symbols))

        for symb in hold_symbols:
            print("checking holds with symbol: " + symb)
            try:
                check_target_status(symb)
            except:
                e = sys.exc_info()[0]
                v = sys.exc_info()[1]
                print("uh oh something went wrong checking hold status " + str(e) + ", val:" + str(v))

        time.sleep(5)

    print("Finished handling all the holds from: " + fpath)

if __name__ == "__main__":

    if (len(sys.argv) == 2):
        filename = sys.argv[1]
        print("Filepath we're looking at: " + filename)
        main(filename)
    else:
        print("Sike wrong number of args: " + str(len(sys.argv)))


# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.6.9 (default, Apr 18 2020, 01:56:04)
# [GCC 8.4.0]
# Embedded file name: build\bdist.win-amd64\egg\arcadeplus\examples\shape_list_demo_person.py
# Compiled at: 2020-03-29 18:07:43
# Size of source mod 2**32: 4577 bytes
"""
Simple program showing how to use a shape list to create a more complex shape
out of basic ones.

If Python and arcadeplus are installed, this example can be run from the command line with:
python -m arcadeplus.examples.shape_list_demo_person
"""
import arcadeplus
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = 'Shape List Demo Person'

def make_person(head_radius, chest_height, chest_width, leg_width, leg_height, arm_width, arm_length, arm_gap, shoulder_height):
    shape_list = arcadeplus.ShapeElementList()
    shape = arcadeplus.create_ellipse_filled(0, chest_height / 2 + head_radius, head_radius, head_radius, arcadeplus.color.WHITE)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(0, 0, chest_width, chest_height, arcadeplus.color.BLACK)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(-(chest_width / 2) + leg_width / 2, -(chest_height / 2) - leg_height / 2, leg_width, leg_height, arcadeplus.color.RED)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(chest_width / 2 - leg_width / 2, -(chest_height / 2) - leg_height / 2, leg_width, leg_height, arcadeplus.color.RED)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(-(chest_width / 2) - arm_width / 2 - arm_gap, chest_height / 2 - arm_length / 2 - shoulder_height, arm_width, arm_length, arcadeplus.color.BLUE)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(-(chest_width / 2) - (arm_gap + arm_width) / 2, chest_height / 2 - shoulder_height / 2, arm_gap + arm_width, shoulder_height, arcadeplus.color.BLUE_BELL)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(chest_width / 2 + arm_width / 2 + arm_gap, chest_height / 2 - arm_length / 2 - shoulder_height, arm_width, arm_length, arcadeplus.color.BLUE)
    shape_list.append(shape)
    shape = arcadeplus.create_rectangle_filled(chest_width / 2 + (arm_gap + arm_width) / 2, chest_height / 2 - shoulder_height / 2, arm_gap + arm_width, shoulder_height, arcadeplus.color.BLUE_BELL)
    shape_list.append(shape)
    return shape_list

class MyGame(arcadeplus.Window):
    __doc__ = ' Main application class. '

    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
        head_radius = 30
        chest_height = 110
        chest_width = 70
        leg_width = 20
        leg_height = 80
        arm_width = 15
        arm_length = 70
        arm_gap = 10
        shoulder_height = 15
        self.shape_list = make_person(head_radius, chest_height, chest_width, leg_width, leg_height, arm_width, arm_length, arm_gap, shoulder_height)
        arcadeplus.set_background_color(arcadeplus.color.AMAZON)

    def setup(self):
        """ Set up the game and initialize the variables. """
        pass

    def on_draw(self):
        """
        Render the screen.
        """
        arcadeplus.start_render()
        self.shape_list.draw()

    def on_update(self, delta_time):
        """ Movement and game logic """
        self.shape_list.center_x += 1
        self.shape_list.center_y += 1
        self.shape_list.angle += 0.1

def main():
    window = MyGame()
    window.setup()
    arcadeplus.run()

if __name__ == '__main__':
    main()


import FWCore.ParameterSet.Config as cms

#Full Event content
SimGeneralFEVTDEBUG = cms.PSet(
    outputCommands = cms.untracked.vstring('drop *_trackingtruthprod_*_*',
        'drop *_electrontruth_*_*',
        'keep *_mix_MergedTrackTruth_*',
        'keep CrossingFramePlaybackInfoNew_*_*_*')
)
#RAW content
SimGeneralRAW = cms.PSet(
    outputCommands = cms.untracked.vstring('keep CrossingFramePlaybackInfoNew_*_*_*',
                                           'keep PileupSummaryInfos_*_*_*',
                                           'keep int_*_bunchSpacing_*')
)
#RECO content
SimGeneralRECO = cms.PSet(
    outputCommands = cms.untracked.vstring('keep PileupSummaryInfos_*_*_*',
                                           'keep int_*_bunchSpacing_*')
)
#AOD content
SimGeneralAOD = cms.PSet(
    outputCommands = cms.untracked.vstring('keep PileupSummaryInfos_*_*_*',
                                           'keep int_*_bunchSpacing_*')
)

def _modifySimGeneralEventContentForHGCal( obj ):
    obj.outputCommands.append('keep *_mix_HGCDigisEE_*')
    obj.outputCommands.append('keep *_mix_HGCDigisHEfront_*')
    obj.outputCommands.append('keep *_mix_HGCDigisHEback_*')

# mods for HGCAL
from Configuration.StandardSequences.Eras import eras
eras.phase2_hgcal.toModify( SimGeneralRAW, func=_modifySimGeneralEventContentForHGCal )
eras.phase2_hgcal.toModify( SimGeneralFEVTDEBUG, func=_modifySimGeneralEventContentForHGCal )
eras.phase2_hgcal.toModify( SimGeneralRECO, func=_modifySimGeneralEventContentForHGCal )


#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""Tests for _qubit_operator.py."""
import copy

import numpy
import pytest

from openfermion.ops._qubit_operator import (_PAULI_OPERATOR_PRODUCTS,
                                             QubitOperator,
                                             QubitOperatorError)

def test_pauli_operator_product_unchanged():
    correct = {('I', 'I'): (1., 'I'),
               ('I', 'X'): (1., 'X'),
               ('X', 'I'): (1., 'X'),
               ('I', 'Y'): (1., 'Y'),
               ('Y', 'I'): (1., 'Y'),
               ('I', 'Z'): (1., 'Z'),
               ('Z', 'I'): (1., 'Z'),
               ('X', 'X'): (1., 'I'),
               ('Y', 'Y'): (1., 'I'),
               ('Z', 'Z'): (1., 'I'),
               ('X', 'Y'): (1.j, 'Z'),
               ('X', 'Z'): (-1.j, 'Y'),
               ('Y', 'X'): (-1.j, 'Z'),
               ('Y', 'Z'): (1.j, 'X'),
               ('Z', 'X'): (1.j, 'Y'),
               ('Z', 'Y'): (-1.j, 'X')}
    assert _PAULI_OPERATOR_PRODUCTS == correct

def test_imul_inplace():
    qubit_op = QubitOperator("X1")
    prev_id = id(qubit_op)
    qubit_op *= 3.
    assert id(qubit_op) == prev_id

@pytest.mark.parametrize("multiplier", [0.5, 0.6j, numpy.float64(2.303),
                         numpy.complex128(-1j)])
def test_imul_scalar(multiplier):
    loc_op = ((1, 'X'), (2, 'Y'))
    qubit_op = QubitOperator(loc_op)
    qubit_op *= multiplier
    assert qubit_op.terms[loc_op] == pytest.approx(multiplier)

def test_imul_qubit_op():
    op1 = QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j)
    op2 = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5)
    op1 *= op2
    correct_coefficient = 1.j * 3.0j * 0.5
    correct_term = ((0, 'Y'), (1, 'X'), (3, 'Z'), (11, 'X'))
    assert len(op1.terms) == 1
    assert correct_term in op1.terms

def test_imul_qubit_op_2():
    op3 = QubitOperator(((1, 'Y'), (0, 'X')), -1j)
    op4 = QubitOperator(((1, 'Y'), (0, 'X'), (2, 'Z')), -1.5)
    op3 *= op4
    op4 *= op3
    assert ((2, 'Z'),) in op3.terms
    assert op3.terms[((2, 'Z'),)] == 1.5j

def test_imul_bidir():
    op_a = QubitOperator(((1, 'Y'), (0, 'X')), -1j)
    op_b = QubitOperator(((1, 'Y'), (0, 'X'), (2, 'Z')), -1.5)
    op_a *= op_b
    op_b *= op_a
    assert ((2, 'Z'),) in op_a.terms
    assert op_a.terms[((2, 'Z'),)] == 1.5j
    assert ((0, 'X'), (1, 'Y')) in op_b.terms
    assert op_b.terms[((0, 'X'), (1, 'Y'))] == -2.25j

def test_imul_bad_multiplier():
    op = QubitOperator(((1, 'Y'), (0, 'X')), -1j)
    with pytest.raises(TypeError):
        op *= "1"

def test_mul_by_scalarzero():
    op = QubitOperator(((1, 'Y'), (0, 'X')), -1j) * 0
    assert ((0, 'X'), (1, 'Y')) in op.terms
    assert op.terms[((0, 'X'), (1, 'Y'))] == pytest.approx(0.0)

def test_mul_bad_multiplier():
    op = QubitOperator(((1, 'Y'), (0, 'X')), -1j)
    with pytest.raises(TypeError):
        op = op * "0.5"

def test_mul_out_of_place():
    op1 = QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j)
    op2 = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5)
    op3 = op1 * op2
    correct_coefficient = 1.j * 3.0j * 0.5
    correct_term = ((0, 'Y'), (1, 'X'), (3, 'Z'), (11, 'X'))
    assert op1 == QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j)
    assert op2 == QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5)
    assert op3 == QubitOperator(correct_term, correct_coefficient)

def test_mul_npfloat64():
    op = QubitOperator(((1, 'X'), (3, 'Y')), 0.5)
    res = op * numpy.float64(0.5)
    assert res == QubitOperator(((1, 'X'), (3, 'Y')), 0.5 * 0.5)

def test_mul_multiple_terms():
    op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5)
    op += QubitOperator(((1, 'Z'), (3, 'X'), (8, 'Z')), 1.2)
    op += QubitOperator(((1, 'Z'), (3, 'Y'), (9, 'Z')), 1.4j)
    res = op * op
    correct = QubitOperator((), 0.5**2 + 1.2**2 + 1.4j**2)
    correct += QubitOperator(((1, 'Y'), (3, 'Z')),
                             2j * 1j * 0.5 * 1.2)
    assert res == correct

def test_renormalize_error():
    op = QubitOperator()
    with pytest.raises(ZeroDivisionError):
        op.renormalize()

def test_renormalize():
    op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 1)
    op += QubitOperator(((2, 'Z'), (3, 'Y')), 1)
    op.renormalize()
    for term in op.terms:
        assert op.terms[term] == pytest.approx(1/numpy.sqrt(2.))
    assert op.induced_norm(2) == pytest.approx(1.)


# coding=utf-8
# Copyright 2017 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
                        unicode_literals, with_statement)

import os
import re

from pants.backend.jvm import argfile
from pants.backend.jvm.subsystems.shader import Shader
from pants.backend.jvm.tasks.nailgun_task import NailgunTask
from pants.base.exceptions import TaskError
from pants.base.revision import Revision
from pants.base.workunit import WorkUnitLabel
from pants.java.jar.jar_dependency import JarDependency
from pants.util.dirutil import safe_mkdir
from pants.util.memo import memoized_property
from pants.util.strutil import safe_shlex_split

class ErrorProne(NailgunTask):
  """Check Java code for Error Prone violations.  See http://errorprone.info/ for more details."""

  _ERRORPRONE_MAIN = 'com.google.errorprone.ErrorProneCompiler'
  _JAVA_SOURCE_EXTENSION = '.java'

  @classmethod
  def register_options(cls, register):
    super(ErrorProne, cls).register_options(register)

    register('--skip', type=bool, help='Skip Error Prone.')
    register('--transitive', default=False, type=bool,
             help='Run Error Prone against transitive dependencies of targets '
                  'specified on the command line.')
    register('--command-line-options', type=list, default=[], fingerprint=True,
             help='Command line options passed to Error Prone')
    register('--exclude-patterns', type=list, default=[], fingerprint=True,
             help='Patterns for targets to be excluded from analysis.')

    cls.register_jvm_tool(register,
                          'errorprone',
                          classpath=[
                            JarDependency(org='com.google.errorprone',
                                          name='error_prone_core',
                                          rev='2.3.1'),
                          ],
                          main=cls._ERRORPRONE_MAIN,
                          custom_rules=[
                            Shader.exclude_package('com.google.errorprone', recursive=True)
                          ]
                         )

    # The javac version should be kept in sync with the version used by errorprone above.
    cls.register_jvm_tool(register,
                          'errorprone-javac',
                          classpath=[
                            JarDependency(org='com.google.errorprone',
                                          name='javac',
                                          rev='9+181-r4173-1'),
                          ])

  @classmethod
  def prepare(cls, options, round_manager):
    super(ErrorProne, cls).prepare(options, round_manager)
    round_manager.require_data('runtime_classpath')

  @memoized_property
  def _exclude_patterns(self):
    return [re.compile(x) for x in set(self.get_options().exclude_patterns or [])]

  def _is_errorprone_target(self, target):
    if not target.has_sources(self._JAVA_SOURCE_EXTENSION):
      self.context.log.debug('Skipping [{}] because it has no {} sources'.format(target.address.spec, self._JAVA_SOURCE_EXTENSION))
      return False
    if target.is_synthetic:
      self.context.log.debug('Skipping [{}] because it is a synthetic target'.format(target.address.spec))
      return False
    for pattern in self._exclude_patterns:
      if pattern.search(target.address.spec):
        self.context.log.debug(
          "Skipping [{}] because it matches exclude pattern '{}'".format(target.address.spec, pattern.pattern))
        return False
    return True

  @property
  def cache_target_dirs(self):
    return True

  def execute(self):
    if self.get_options().skip:
      return

    if self.get_options().transitive:
      targets = self.context.targets(self._is_errorprone_target)
    else:
      targets = filter(self._is_errorprone_target, self.context.target_roots)

    targets = list(set(targets))

    target_count = 0
    errorprone_failed = False
    with self.invalidated(targets, invalidate_dependents=True) as invalidation_check:
      total_targets = len(invalidation_check.invalid_vts)
      for vt in invalidation_check.invalid_vts:
        target_count += 1
        self.context.log.info('[{}/{}] {}'.format(
          str(target_count).rjust(len(str(total_targets))),
          total_targets,
          vt.target.address.spec))

        result = self.errorprone(vt.target)
        if result != 0:
          errorprone_failed = True
          if self.get_options().fail_fast:
            break
        else:
          vt.update()

      if errorprone_failed:
        raise TaskError('ErrorProne checks failed')

  def calculate_sources(self, target):
    return {source for source in target.sources_relative_to_buildroot()
            if source.endswith(self._JAVA_SOURCE_EXTENSION)}

  def errorprone(self, target):
    runtime_classpaths = self.context.products.get_data('runtime_classpath')
    runtime_classpath = [jar for conf, jar in runtime_classpaths.get_for_targets(target.closure(bfs=True))]

    output_dir = os.path.join(self.workdir, target.id)
    safe_mkdir(output_dir)
    runtime_classpath.append(output_dir)

    # Try to run errorprone with the same java version as the target
    # The minimum JDK for errorprone is JDK 1.8
    min_jdk_version = max(target.platform.target_level, Revision.lenient('1.8'))
    if min_jdk_version.components[0] == 1:
      max_jdk_version = Revision(min_jdk_version.components[0], min_jdk_version.components[1], '9999')
    else:
      max_jdk_version = Revision(min_jdk_version.components[0], '9999')
    self.set_distribution(minimum_version=min_jdk_version, maximum_version=max_jdk_version, jdk=True)

    jvm_options = self.get_options().jvm_options[:]
    if self.dist.version < Revision.lenient('9'):
      # For Java 8 we need to add the errorprone javac jar to the bootclasspath to
      # avoid the "java.lang.NoSuchFieldError: ANNOTATION_PROCESSOR_MODULE_PATH" error
      # See https://github.com/google/error-prone/issues/653 for more information
      jvm_options.extend(['-Xbootclasspath/p:{}'.format(self.tool_classpath('errorprone-javac')[0])])

    args = [
      '-d', output_dir,
    ]

    # Errorprone does not recognize source or target 10 yet
    if target.platform.source_level < Revision.lenient('10'):
      args.extend(['-source', str(target.platform.source_level)])

    if target.platform.target_level < Revision.lenient('10'):
      args.extend(['-target', str(target.platform.target_level)])

    errorprone_classpath_file = os.path.join(self.workdir, '{}.classpath'.format(os.path.basename(output_dir)))
    with open(errorprone_classpath_file, 'w') as f:
      f.write('-classpath ')
      f.write(':'.join(runtime_classpath))
    args.append('@{}'.format(errorprone_classpath_file))

    for opt in self.get_options().command_line_options:
      args.extend(safe_shlex_split(opt))

    with argfile.safe_args(self.calculate_sources(target), self.get_options()) as batched_sources:
      args.extend(batched_sources)
      result = self.runjava(classpath=self.tool_classpath('errorprone'),
                            main=self._ERRORPRONE_MAIN,
                            jvm_options=jvm_options,
                            args=args,
                            workunit_name='errorprone',
                            workunit_labels=[WorkUnitLabel.LINT])

      self.context.log.debug('java {main} ... exited with result ({result})'.format(
        main=self._ERRORPRONE_MAIN, result=result))

    return result


"""Program to get tweets and send data to eventhub."""

import tweepy
from azure.servicebus import ServiceBusService
import getSentiment as s
import config as c

consumer_key = c.consumer_key
consumer_secret = c.consumer_secret
access_token = c.access_token
access_token_secret = c.access_token_secret

namespace = c.namespace
eventhub = c.eventhub
keyName = c.keyName
keyValue = c.keyValue

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

sbs = ServiceBusService(service_namespace=namespace,
                        shared_access_key_name=keyName,
                        shared_access_key_value=keyValue)

class TweetStreamListener(tweepy.StreamListener):
    """Creating TweetStreamListener class."""

    def on_status(self, status):
        """A function to get tweet and sentiment + sends to eventhub."""
        valueToSend = s.getSentiment(status.text)
        sbs.send_event(eventhub, valueToSend)

    def on_error(self, status_code):
        """Function which handles with error."""
        if status_code == 420:
            return False

tweetyStreamListener = TweetStreamListener()
stream = tweepy.Stream(auth, tweetyStreamListener)

stream.filter(track=c.filter)


# pre sum, time/space O(n)

class Solution(object):
    def maxSubArrayLen(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        presums = {0: -1}  # {presum: index}
        max_len = 0
        total = 0
        for i, num in enumerate(nums):
            total += num
            if total - k in presums:
                max_len = max(max_len, i - presums[total - k])
            if total not in presums:
                presums[total] = i
        return max_len

"""
Given an array nums and a target value k, find the maximum length of a subarray that sums to k.
If there isn't one, return 0 instead.

Note:
The sum of the entire nums array is guaranteed to fit within the 32-bit signed integer range.

Example 1:

Input: nums = [1, -1, 5, -2, 3], k = 3
Output: 4
Explanation: The subarray [1, -1, 5, -2] sums to 3 and is the longest.
Example 2:

Input: nums = [-2, -1, 2, 1], k = 1
Output: 2
Explanation: The subarray [-1, 2] sums to 1 and is the longest.
Follow Up:
Can you do it in O(n) time?
"""

Templates/0325. Maximum Size Subarray Sum Equals k.py


# 1011번 문제 : Fly me to the Alpha Centauri

test = int(input())

for i in range(test):
    x, y = map(int,input().split())
    distance = y - x
    count = 0  # 이동 횟수
    move = 1  # count별 이동 가능한 거리
    move_sum = 0  # 이동한 거리의 합
    while move_sum < distance :
        count += 1
        move_sum += move  # count 수에 해당하는 move를 더함
        if count % 2 == 0 :  # count가 2의 배수일 때,
            move += 1
    print(count)


#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Pavel Korshunov <pavel.korshunov@idiap.ch>
# Wed 19 Aug 13:43:50 2015

"""Dumps lists of files.
"""

import os
import sys

# Driver API
# ==========

def dumplist(args):
  """Dumps lists of files based on your criteria"""

  from .query import Database
  db = Database()

  r = db.objects(
      protocol=args.protocol,
      support=args.support,
      groups=args.group,
      purposes=args.purposes,
      clients=args.client,
      )

  output = sys.stdout
  if args.selftest:
    from bob.db.base.utils import null
    output = null()

  for f in r:
    output.write('%s\n' % (f.make_path(args.directory, args.extension),))

  return 0

def add_command(subparsers):
  """Add specific subcommands that the action "dumplist" can use"""

  from argparse import SUPPRESS

  parser = subparsers.add_parser('dumplist', help=dumplist.__doc__)

  from .query import Database

  db = Database()

  if not db.is_valid():
    protocols = ('waiting','for','database','creation')
    clients = tuple()
  else:
    protocols = [k.name for k in db.protocols()]
    clients = [k.id for k in db.clients()]

  parser.add_argument('-d', '--directory', dest="directory", default='',
                      help="if given, this path will be prepended to every entry returned (defaults to '%(default)s')")
  parser.add_argument('-e', '--extension', dest="extension", default='',
                      help="if given, this extension will be appended to every entry returned (defaults to '%(default)s')")
  parser.add_argument('-c', '--purpose', dest="purposes", default=None,
                      help="if given, limits the dump to a particular subset of the data that corresponds to the "
                           "given purpose (defaults to '%(default)s')", choices=db.purposes())
  parser.add_argument('-g', '--group', dest="group", default=None,
                      help="if given, this value will limit the output files to those belonging to a particular "
                           "protocol group. (defaults to '%(default)s')", choices=db.groups())
  parser.add_argument('-s', '--support', dest="support", default=None,
                      help="if given, this value will limit the output files to those using this type of attack "
                           "support. (defaults to '%(default)s')", choices=db.attack_supports())
  parser.add_argument('-x', '--protocol', dest="protocol", default=None,
                      help="if given, this value will limit the output files to those for a given protocol. "
                           "(defaults to '%(default)s')", choices=protocols)
  parser.add_argument('-C', '--client', dest="client", default=None, type=str,
                      help="if given, limits the dump to a particular client (defaults to '%(default)s')", choices=clients)
  parser.add_argument('--self-test', dest="selftest", default=False, action='store_true', help=SUPPRESS)

  parser.set_defaults(func=dumplist) #action


from grab import Grab
import re, os, logging

mail_regex = r'\b[\w.-]+?@\w+?\.\w+?\b'
phone_regex = r'\b[8-9]{0,2}[\-\(\)\d\s]{9,18}\b'

DIR = os.path.abspath(os.curdir) + '/'
logging.basicConfig(level=logging.DEBUG)

def get_yandex_urls_by_page_num(query, page=0):
    try:
        g = Grab()
        #g.proxylist.load_file(DIR + 'proxy.txt') #for proxy
        g.go('http://yandex.ru/yandsearch?p=%s&text=%s' % (page, query))
        #print(g.response.body)
        return [x.attr('href') for x in g.doc.select('//a[@class="link organic__url link link_cropped_no"]')]
    except:
        print('get_yandex_urls_by_page_num error', page)
        return []

def get_yandex_urls_by_sup(query, sup=1):
    try:
        urls = []
        for i in range(0, sup):
            urls += get_yandex_urls_by_page_num(query=query, page=i)
        return set(urls)
    except:
        print('get_yandex_urls_by_sup error')
        return ()

def get_email_by_url(url):
    try:
        g = Grab()
        g.go(url)
        return set(re.findall(mail_regex, str(g.response.body)))
    except:
        print('get_email_by_url error')
        return ()

def get_phone_by_url(url):
    try:
        g = Grab()
        g.go(url)
        return set(re.findall(phone_regex, str(g.response.body)))
    except:
        print('get_phone_by_url error')
        return ()

def get_contacts(url):
    try:
        return list(get_email_by_url(url)) +  list(get_phone_by_url(url))
    except:
        print('get_contacts error')
        return ()

if __name__ == "__main__":
    pass


'''
Problem 5

2520 is the smallest number that can be divided by each
of the numbers from 1 to 10 without any remainder.

What is the smallest positive number that is evenly
divisible by all of the numbers from 1 to 20?
'''
from __future__ import division
from eulerlib import primes

def smallest_multiple(n):
    x = 1
    for i in range(1, n+1):
        if x % i != 0:
            p = primes(i-1)
            y = i
            for j in p:
                if i % j == 0:
                    y = y/j
            x = x * y
    assert int(x) == 2*2*2*2*3*3*5*7*11*13*17*19
    return str(int(x))

if __name__ == '__main__':
    print(smallest_multiple(20))