#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Dusan Klinec, ph4r05, 2018
#
# Educational use only.
# One pass signature, not maintained
# DEPRECATED

from monero_serialize import xmrtypes, xmrserialize
from monero_glue.xmr.monero import classify_subaddresses
from monero_glue.xmr.tsx_data import TsxData
from monero_glue.xmr import monero, mlsag2, ring_ct, crypto, common as common

class Trezor(object):
    """
    Main Trezor object
    """

    def __init__(self):
        self.tsx_ctr = 0
        self.tsx_obj = None  # type: TTransaction
        self.creds = None  # type: WalletCreds

    async def tsx_init(self, tsx_data: TsxData):
        self.tsx_ctr += 1
        self.tsx_obj = TTransaction(self)
        self.tsx_obj.creds = self.creds
        return await self.tsx_obj.init_transaction(tsx_data, self.tsx_ctr)

    async def precompute_subaddr(self, account, indices):
        """
        Precomputes subaddresses for account (major) and list of indices (minors)
        :param account:
        :param indices:
        :return:
        """
        return self.tsx_obj.precompute_subaddr(account, indices)

    async def tsx_set_input(self, src_entr):
        """
        :param src_entr
        :type src_entr: xmrtypes.TxSourceEntry
        :return:
        """
        return await self.tsx_obj.set_input(src_entr)

    async def tsx_inputs_done(self):
        """
        All inputs set
        :return:
        """
        return await self.tsx_obj.tsx_inputs_done()

    async def tsx_set_output1(self, dst_entr):
        """
        :param src_entr
        :type src_entr: xmrtypes.TxDestinationEntry
        :return:
        """
        return await self.tsx_obj.set_out1(dst_entr)

class TTransaction(object):
    """
    Transaction builder
    """

    def __init__(self, trezor=None):
        self.trezor = trezor  # type: Trezor
        self.creds = None  # type: monero.AccountCreds
        self.key_master = None
        self.key_hmac = None

        self.r = None  # txkey
        self.r_pub = None

        self.tsx_data = None  # type: monero.TsxData
        self.need_additional_txkeys = False
        self.use_bulletproof = False
        self.use_rct = True
        self.use_simple_rct = False
        self.additional_tx_keys = []
        self.additional_tx_public_keys = []
        self.inp_idx = -1
        self.out_idx = -1
        self.summary_inputs_money = 0
        self.summary_outs_money = 0
        self.input_rcts = []
        self.input_secrets = []
        self.output_secrets = []
        self.subaddresses = {}
        self.tx = xmrtypes.Transaction(vin=[], vout=[], extra=b"")
        self.source_permutation = []  # sorted by key images
        self.tx_prefix_hash = None

    def gen_r(self):
        """
        Generates a new transaction key pair.
        :return:
        """
        self.r = crypto.random_scalar()
        self.r_pub = crypto.scalarmult_base(self.r)

    async def init_transaction(self, tsx_data, tsx_ctr):
        """
        Initializes a new transaction.
        :param tsx_data:
        :param tsx_ctr:
        :return:
        """
        self.tsx_data = tsx_data
        self.gen_r()

        # Additional keys
        class_res = classify_subaddresses(
            tsx_data.outputs, tsx_data.change_dts.addr if tsx_data.change_dts else None
        )
        num_stdaddresses, num_subaddresses, single_dest_subaddress = class_res

        # if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D
        if num_stdaddresses == 0 and num_subaddresses == 1:
            self.r_pub = crypto.ge_scalarmult(
                self.r, crypto.decodepoint(single_dest_subaddress.m_spend_public_key)
            )

        self.need_additional_txkeys = num_subaddresses > 0 and (
            num_stdaddresses > 0 or num_subaddresses > 1
        )
        if self.need_additional_txkeys:
            for _ in range(len(tsx_data.outputs)):
                self.additional_tx_keys.append(crypto.random_scalar())

        # Extra processing, payment id
        self.tx.version = 2
        self.tx.unlock_time = tsx_data.unlock_time
        await self.process_payment_id()
        await self.compute_hmac_keys(tsx_ctr)

    async def process_payment_id(self):
        """
        Payment id -> extra
        :return:
        """
        if self.tsx_data.payment_id is None or len(self.tsx_data.payment_id) == 0:
            return

        change_addr = (
            self.tsx_data.change_dts.addr if self.tsx_data.change_dts else None
        )
        view_key_pub_enc = monero.get_destination_view_key_pub(
            self.tsx_data.outputs, change_addr
        )
        if view_key_pub_enc == crypto.NULL_KEY_ENC:
            raise ValueError(
                "Destinations have to have exactly one output to support encrypted payment ids"
            )

        view_key_pub = crypto.decodepoint(view_key_pub_enc)
        payment_id_encr = monero.encrypt_payment_id(
            self.tsx_data.payment_id, view_key_pub, self.r
        )

        extra_nonce = monero.set_encrypted_payment_id_to_tx_extra_nonce(payment_id_encr)
        self.tx.extra = monero.add_extra_nonce_to_tx_extra(b"", extra_nonce)

    async def compute_hmac_keys(self, tsx_ctr):
        """
        Generate master key H(TsxData || r || c_tsx)
        :return:
        """
        writer = monero.get_keccak_writer()
        ar1 = xmrserialize.Archive(writer, True)
        await ar1.message(self.tsx_data)
        await writer.awrite(crypto.encodeint(self.r))
        await xmrserialize.dump_uvarint(writer, tsx_ctr)
        self.key_master = writer.get_digest()
        self.key_hmac = crypto.keccak_hash(b"hmac" + self.key_master)

    def precompute_subaddr(self, account, indices):
        """
        Precomputes subaddresses for account (major) and list of indices (minors)
        Subaddresses have to be stored in encoded form - unique representation.
        Single point can have multiple extended coordinates representation - would not match during subaddress search.
        :param account:
        :param indices:
        :return:
        """
        for idx in indices:
            if account == 0 and idx == 0:
                self.subaddresses[
                    crypto.encodepoint(self.trezor.creds.spend_key_public)
                ] = (0, 0)
                continue

            pub = monero.get_subaddress_spend_public_key(
                self.trezor.creds.view_key_private,
                self.trezor.creds.spend_key_public,
                major=account,
                minor=idx,
            )
            pub = crypto.encodepoint(pub)
            self.subaddresses[pub] = (account, idx)

    async def set_input(self, src_entr):
        """
        :param src_entr:
        :type src_entr: xmrtypes.TxSourceEntry
        :return:
        """
        self.inp_idx += 1
        if src_entr.real_output >= len(src_entr.outputs):
            raise ValueError(
                "real_output index %s bigger than output_keys.size()"
                % (src_entr.real_output, len(src_entr.outputs))
            )
        self.summary_inputs_money += src_entr.amount

        out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest)
        tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
        additional_keys = [
            crypto.decodepoint(x) for x in src_entr.real_out_additional_tx_keys
        ]

        secs = monero.generate_key_image_helper(
            self.trezor.creds,
            self.subaddresses,
            out_key,
            tx_key,
            additional_keys,
            src_entr.real_output_in_tx_index,
        )
        xi, ki, di = secs
        self.input_secrets.append((xi,))
        self.input_rcts.append(src_entr.rct)

        # Construct tx.vin
        vini = xmrtypes.TxinToKey(
            amount=src_entr.amount, k_image=crypto.encodepoint(ki)
        )
        vini.key_offsets = [x[0] for x in src_entr.outputs]
        vini.key_offsets = monero.absolute_output_offsets_to_relative(vini.key_offsets)
        self.tx.vin.append(vini)

        # HMAC(T_in,i || vin_i)
        kwriter = monero.get_keccak_writer()
        ar = xmrserialize.Archive(kwriter, True)
        await ar.message(src_entr, xmrtypes.TxSourceEntry)
        await ar.message(vini, xmrtypes.TxinToKey)

        hmac_key_vini = crypto.keccak_hash(
            self.key_hmac + b"txin" + xmrserialize.dump_uvarint_b(self.inp_idx)
        )
        hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest())

        return vini, hmac_vini

    async def tsx_inputs_done(self):
        """
        All inputs set
        :return:
        """

        # Sort tx.in by key image
        self.source_permutation = list(range(self.inp_idx + 1))
        self.source_permutation.sort(key=lambda x: self.tx.vin[x].k_image)

        def swapper(x, y):
            self.tx.vin[x], self.tx.vin[y] = self.tx.vin[y], self.tx.vin[x]
            self.input_secrets[x], self.input_secrets[y] = (
                self.input_secrets[y],
                self.input_secrets[x],
            )
            self.input_rcts[x], self.input_rcts[y] = (
                self.input_rcts[y],
                self.input_rcts[x],
            )

        common.apply_permutation(self.source_permutation, swapper)

        # Set public key to the extra
        self.tx.extra = monero.add_tx_pub_key_to_extra(self.tx.extra, self.r_pub)

        self.use_simple_rct = self.inp_idx > 0

    async def set_out1(self, dst_entr):
        """
        Set destination entry
        :param src_entr
        :type src_entr: xmrtypes.TxDestinationEntry
        :return:
        """
        self.out_idx += 1
        change_addr = (
            self.tsx_data.change_dts.addr if self.tsx_data.change_dts else None
        )

        if dst_entr.amount <= 0 and self.tx.version <= 1:
            raise ValueError("Destination with wrong amount: %s" % dst_entr.amount)

        if self.need_additional_txkeys:
            if dst_entr.is_subaddress:
                additional_txkey = crypto.ge_scalarmult(
                    self.additional_tx_keys[self.out_idx],
                    crypto.decodepoint(dst_entr.addr.m_spend_public_key),
                )
            else:
                additional_txkey = crypto.ge_scalarmult_base(
                    self.additional_tx_keys[self.out_idx]
                )

            self.additional_tx_public_keys.append(additional_txkey)

        if change_addr and dst_entr.addr == change_addr:
            # sending change to yourself; derivation = a*R
            derivation = monero.generate_key_derivation(
                self.r_pub, self.creds.view_key_private
            )

        else:
            # sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
            deriv_priv = (
                self.additional_tx_keys[self.out_idx]
                if dst_entr.is_subaddress and self.need_additional_txkeys
                else self.r
            )
            derivation = monero.generate_key_derivation(
                crypto.decodepoint(dst_entr.addr.m_view_public_key), deriv_priv
            )

        amount_key = crypto.derivation_to_scalar(derivation, self.out_idx)
        tx_out_key = crypto.derive_public_key(
            derivation,
            self.out_idx,
            crypto.decodepoint(dst_entr.addr.m_spend_public_key),
        )
        tk = xmrtypes.TxoutToKey(key=crypto.encodepoint(tx_out_key))
        tx_out = xmrtypes.TxOut(amount=dst_entr.amount, target=tk)
        self.tx.vout.append(tx_out)
        self.summary_outs_money += dst_entr.amount

        self.output_secrets.append((amount_key,))

        # Last output?
        if self.out_idx + 1 == len(self.tsx_data.outputs):
            await self.all_out1_set()

    async def all_out1_set(self):
        """
        All out1 set phase
        Adds additional public keys to the tx.extra
        :return:
        """
        # self.tx.extra = await monero.remove_field_from_tx_extra(self.tx.extra, xmrtypes.TxExtraAdditionalPubKeys)
        if self.need_additional_txkeys:
            self.tx.extra = await monero.add_additional_tx_pub_keys_to_extra(
                self.tx.extra, self.additional_tx_public_keys
            )

        if self.summary_outs_money > self.summary_inputs_money:
            raise ValueError(
                "Transaction inputs money (%s) less than outputs money (%s)"
                % (self.summary_inputs_money, self.summary_outs_money)
            )

    async def signature(self, tx):
        """
        Computes the signature in one pass.
        Implements RingCT in Python.

        :param tx: const data
        :type tx: xmrtypes.TxConstructionData
        :return:
        """
        amount_in = 0
        inamounts = [None] * len(self.source_permutation)
        index = [None] * len(self.source_permutation)
        in_sk = [None] * len(self.source_permutation)  # type: list[xmrtypes.CtKey]

        for i in range(len(self.source_permutation)):
            idx = self.source_permutation[i]
            src = tx.sources[idx]
            amount_in += src.amount
            inamounts[i] = src.amount
            index[i] = src.real_output
            in_sk[i] = xmrtypes.CtKey(
                dest=self.input_secrets[i][0], mask=crypto.decodeint(src.mask)
            )
            # TODO: kLRki

            # private key correctness test
            if __debug__:
                assert crypto.point_eq(
                    crypto.decodepoint(src.outputs[src.real_output][1].dest),
                    crypto.scalarmult_base(in_sk[i].dest),
                )
                assert crypto.point_eq(
                    crypto.decodepoint(src.outputs[src.real_output][1].mask),
                    crypto.gen_c(in_sk[i].mask, inamounts[i]),
                )

        destinations = []
        outamounts = []
        amount_out = 0
        for idx, dst in enumerate(tx.splitted_dsts):
            destinations.append(crypto.decodepoint(self.tx.vout[idx].target.key))
            outamounts.append(self.tx.vout[idx].amount)
            amount_out += self.tx.vout[idx].amount

        if self.use_simple_rct:
            mix_ring = [None] * (self.inp_idx + 1)
            for i in range(len(self.source_permutation)):
                src = tx.sources[self.source_permutation[i]]
                mix_ring[i] = []
                for idx2, out in enumerate(src.outputs):
                    mix_ring[i].append(out[1])

        else:
            n_total_outs = len(tx.sources[0].outputs)
            mix_ring = [None] * n_total_outs
            for idx in range(n_total_outs):
                mix_ring[idx] = []
                for i in range(len(self.source_permutation)):
                    src = tx.sources[self.source_permutation[i]]
                    mix_ring[idx].append(src.outputs[idx][1])

        if not self.use_simple_rct and amount_in > amount_out:
            outamounts.append(amount_in - amount_out)

        # Hide amounts
        self.zero_out_amounts()

        # Tx prefix hash
        await self.compute_tx_prefix_hash()

        # Signature
        if self.use_simple_rct:
            rv = await self.gen_rct_simple(
                in_sk,
                destinations,
                inamounts,
                outamounts,
                amount_in - amount_out,
                mix_ring,
                None,
                None,
                index,
            )
        else:
            rv = await self.gen_rct(
                in_sk,
                destinations,
                outamounts,
                mix_ring,
                None,
                None,
                tx.sources[0].real_output,
            )

        # Recode for serialization
        rv = monero.recode_rct(rv, encode=True)
        self.tx.signatures = []
        self.tx.rct_signatures = rv
        del rv

        # Serialize response
        writer = xmrserialize.MemoryReaderWriter()
        ar1 = xmrserialize.Archive(writer, True)
        await ar1.message(self.tx, msg_type=xmrtypes.Transaction)

        return bytes(writer.get_buffer())

    def zero_out_amounts(self):
        """
        Zero out all amounts to mask rct outputs, real amounts are now encrypted
        :return:
        """
        for idx, inx in enumerate(self.tx.vin):
            if self.input_rcts[idx]:
                inx.amount = 0

        for out in self.tx.vout:
            out.amount = 0

    async def compute_tx_prefix_hash(self):
        """
        Computes tx prefix hash
        :return:
        """
        self.tx_prefix_hash = await monero.get_transaction_prefix_hash(self.tx)
        return self.tx_prefix_hash

    async def gen_rct_header(self, destinations, outamounts):
        """
        Initializes RV RctSig structure, processes outputs, computes range proofs, ecdh info masking.
        Common to gen_rct and gen_rct_simple.

        :param destinations:
        :param outamounts:
        :return:
        """
        rv = xmrtypes.RctSig()
        rv.p = xmrtypes.RctSigPrunable()

        rv.message = self.tx_prefix_hash
        rv.outPk = [None] * len(destinations)

        if self.use_bulletproof:
            rv.p.bulletproofs = [None] * len(destinations)
        else:
            rv.p.rangeSigs = [None] * len(destinations)
        rv.ecdhInfo = [None] * len(destinations)

        # Output processing
        sumout = crypto.sc_0()
        out_sk = [None] * len(destinations)
        for idx in range(len(destinations)):
            rv.outPk[idx] = xmrtypes.CtKey(dest=crypto.encodepoint(destinations[idx]))
            C, mask, rsig = None, 0, None

            # Rangeproof
            if self.use_bulletproof:
                raise ValueError("Bulletproof not yet supported")

            else:
                C, mask, rsig = ring_ct.prove_range(outamounts[idx])
                rv.p.rangeSigs[idx] = rsig
                if __debug__:
                    assert ring_ct.ver_range(C, rsig)
                    assert crypto.point_eq(
                        C,
                        crypto.point_add(
                            crypto.scalarmult_base(mask),
                            crypto.scalarmult_h(outamounts[idx]),
                        ),
                    )

            # Mask sum
            rv.outPk[idx].mask = crypto.encodepoint(C)
            sumout = crypto.sc_add(sumout, mask)
            out_sk[idx] = xmrtypes.CtKey(mask=mask)

            # ECDH masking
            amount_key = crypto.encodeint(self.output_secrets[idx][0])
            rv.ecdhInfo[idx] = xmrtypes.EcdhTuple(
                mask=mask, amount=crypto.sc_init(outamounts[idx])
            )
            rv.ecdhInfo[idx] = ring_ct.ecdh_encode(
                rv.ecdhInfo[idx], derivation=amount_key
            )
            monero.recode_ecdh(rv.ecdhInfo[idx], encode=True)

        return rv, sumout, out_sk

    async def gen_rct(
        self, in_sk, destinations, amounts, mix_ring, kLRki, msout, index
    ):
        """
        Full ring CT signature.
        Used when there is only one input transaction to spend.

        :param in_sk:
        :param destinations:
        :param amounts:
        :param mix_ring:
        :param kLRki:
        :param msout:
        :param index:
        :param out_sk:
        :return:
        """
        if len(amounts) != len(destinations) and len(amounts) != len(destinations) + 1:
            raise ValueError("Different number of amounts/destinations")
        if len(self.output_secrets) != len(destinations):
            raise ValueError("Different number of amount_keys/destinations")
        if index >= len(mix_ring):
            raise ValueError("Bad index into mix ring")
        for n in range(len(mix_ring)):
            if len(mix_ring[n]) != len(in_sk):
                raise ValueError("Bad mixring size")
        if (not kLRki or not msout) and (kLRki or msout):
            raise ValueError("Only one of kLRki/mscout is present")

        rv, sumout, out_sk = await self.gen_rct_header(destinations, amounts)
        rv.type = (
            xmrtypes.RctType.FullBulletproof
            if self.use_bulletproof
            else xmrtypes.RctType.Full
        )

        if len(amounts) > len(destinations):
            rv.txnFee = amounts[len(destinations)]
        else:
            rv.txnFee = 0

        txn_fee_key = crypto.scalarmult_h(rv.txnFee)
        rv.mixRing = mix_ring
        # TODO: msout multisig

        full_message = await monero.get_pre_mlsag_hash(rv)
        mg, msc = mlsag2.prove_rct_mg(
            full_message,
            rv.mixRing,
            in_sk,
            out_sk,
            rv.outPk,
            kLRki,
            None,
            index,
            txn_fee_key,
        )
        rv.p.MGs = [mg]

        if __debug__:
            assert mlsag2.ver_rct_mg(
                rv.p.MGs[0], rv.mixRing, rv.outPk, txn_fee_key, full_message
            )
        return rv

    async def gen_rct_simple(
        self,
        in_sk,
        destinations,
        inamounts,
        outamounts,
        txn_fee,
        mix_ring,
        kLRki,
        msout,
        index,
    ):
        """
        Generate simple RCT signature.

        :param in_sk:
        :param destinations:
        :param inamounts:
        :param outamounts:
        :param txn_fee:
        :param mix_ring:
        :param kLRki:
        :param msout:
        :param index:
        :param out_sk:
        :return:
        """
        if len(inamounts) == 0:
            raise ValueError("Empty inamounts")
        if len(inamounts) != len(in_sk):
            raise ValueError("Different number of inamounts/inSk")
        if len(outamounts) != len(destinations):
            raise ValueError("Different number of amounts/destinations")
        if len(self.output_secrets) != len(destinations):
            raise ValueError("Different number of amount_keys/destinations")
        if len(index) != len(in_sk):
            raise ValueError("Different number of index/inSk")
        if len(mix_ring) != len(in_sk):
            raise ValueError("Different number of mixRing/inSk")
        for idx in range(len(mix_ring)):
            if index[idx] >= len(mix_ring[idx]):
                raise ValueError("Bad index into mixRing")

        rv, sumout, out_sk = await self.gen_rct_header(destinations, outamounts)
        rv.type = (
            xmrtypes.RctType.SimpleBulletproof
            if self.use_bulletproof
            else xmrtypes.RctType.Simple
        )
        rv.txnFee = txn_fee
        rv.mixRing = mix_ring

        # Pseudooutputs
        pseudo_outs = [None] * len(inamounts)
        rv.p.MGs = [None] * len(inamounts)
        sumpouts = crypto.sc_0()
        a = []
        for idx in range(len(inamounts) - 1):
            a.append(crypto.random_scalar())
            sumpouts = crypto.sc_add(sumpouts, a[idx])
            pseudo_outs[idx] = crypto.gen_c(a[idx], inamounts[idx])

        a.append(crypto.sc_sub(sumout, sumpouts))
        pseudo_outs[-1] = crypto.gen_c(a[-1], inamounts[-1])

        if self.use_bulletproof:
            rv.p.pseudoOuts = [crypto.encodepoint(x) for x in pseudo_outs]
        else:
            rv.pseudoOuts = [crypto.encodepoint(x) for x in pseudo_outs]

        full_message = await monero.get_pre_mlsag_hash(rv)

        # TODO: msout multisig
        for i in range(len(inamounts)):
            rv.p.MGs[i], msc = mlsag2.prove_rct_mg_simple(
                full_message,
                rv.mixRing[i],
                in_sk[i],
                a[i],
                pseudo_outs[i],
                kLRki[i] if kLRki else None,
                None,
                index[i],
            )

            if __debug__:
                assert mlsag2.ver_rct_mg_simple(
                    full_message, rv.p.MGs[i], rv.mixRing[i], pseudo_outs[i]
                )

        return rv


"""
Memory RNN implementation that follows the paper
Mikolov et al - "Learning Longer Memory in Recurrent Neural Networks", 2014.

It doesn't contain any training or optimization routines.
This code just symbolicly specifies network architecture.

(c) Konstantin Selivanov

"""
import numpy as np
import theano
from theano import tensor as T
from deepml.initializations import orthogonal, glorot_uniform
from theano.tensor.nnet import hard_sigmoid

floatX = theano.config.floatX

def mem_rnn(x, n_in, n_hid, n_out, alpha=0.5, iv=0.01):

    x = x.dimshuffle((1,0,2))

    # hidden cells
    #W_hh = np.random.normal(iv, size=(n_hid,n_hid))
    #W_hh *= 1.25 / max(abs(np.linalg.eigvals(W_hh)))
    W_ih = glorot_uniform(shape=(n_in, n_hid))
    W_hh = orthogonal(shape=(n_hid, n_hid))
    W_ho = glorot_uniform(shape=(n_hid, n_out))
    b_hh = np.zeros(n_hid)
    b_ho = np.zeros(n_out)

    #W_ih = np.random.normal(iv, size=(n_in, n_hid))
    #W_ho = np.random.normal(iv, size=(n_hid, n_out))
    #b_hh = np.zeros(n_hid)
    #b_ho = np.zeros(n_out)

    # long-memory cells

    W_is = glorot_uniform(shape=(n_in, n_hid))
    W_so = glorot_uniform(shape=(n_hid, n_out))
    W_sh = orthogonal(shape=(n_hid, n_hid))

    #W_is = np.random.normal(iv, size=(n_in, n_hid))
    #W_so = np.random.normal(iv, size=(n_hid, n_out))
    #W_sh = np.random.normal(iv, size=(n_hid, n_hid))

    h0 = T.alloc(np.cast[floatX](0.), x.shape[1], n_hid)
    s0 = T.alloc(np.cast[floatX](0.), x.shape[1], n_hid)

    params = [W_ih, W_hh, W_ho, W_is, W_so, W_sh, b_hh, b_ho]
    shared_params = [ theano.shared(p.astype(floatX)) for p in params ]

    # refactor
    [W_ih, W_hh, W_ho, W_is, W_so, W_sh, b_hh, b_ho] = shared_params

    #x = T.bmatrix('x')
    #y = T.bscalar('y')

    def forward(x, h_tm1, s_tm1):
        s = (1 - alpha)*T.dot(x, W_is) + alpha*s_tm1
        h = T.nnet.hard_sigmoid (
            T.dot(h_tm1, W_hh) + T.dot(x, W_ih) + T.dot(s, W_sh) + b_hh )
        return h, s

    [hv, sv], _ = theano.scan(
                    fn=forward,
                    sequences=x,
                    outputs_info=[h0,s0]
                )

    out = T.nnet.sigmoid( T.dot(hv[-1], W_ho) + T.dot(sv[-1], W_so) + b_ho )

    # no need this just for last item
    #out = out.dimshuffle((1,0,2))

    return out, shared_params

if __name__ == '__main__':
    pass


############################################################
# -*- coding: utf-8 -*-
#
# Python-based Tool for interaction with the 10micron mounts
# GUI with PyQT5 for python
# Python  v3.5
#
# Michael Würtenberger
# (c) 2016, 2017
#
# Licence APL2.0
#
############################################################
import logging
import re
import ipaddress
from baseclasses import widget

class CheckIP(widget.MwWidget):
    logger = logging.getLogger(__name__)

    def __init__(self):
        pass

    def checkPort(self, uiPort):
        cursorPosition = uiPort.cursorPosition()
        if uiPort.text().strip() != '':
            port = int(uiPort.text())
        else:
            port = 0
        if 1 < port < 64535:
            valid = True
            uiPort.setStyleSheet(self.TEXT_COLOR_BLUE)
        else:
            valid = False
            uiPort.setStyleSheet(self.TEXT_COLOR_RED)
        uiPort.setText('{0}'.format(port))
        uiPort.setCursorPosition(cursorPosition)
        return valid, port

    def checkIP(self, uiIp):
        cursorPosition = uiIp.cursorPosition()
        IP = uiIp.text().strip()
        try:
            ipaddress.ip_address(IP)
            valid = True
            uiIp.setStyleSheet(self.TEXT_COLOR_BLUE)
        except Exception as e:
            valid = False
            uiIp.setStyleSheet(self.TEXT_COLOR_RED)
        finally:
            pass
        uiIp.setText('{0}'.format(IP))
        uiIp.setCursorPosition(cursorPosition)
        return valid, IP

    def checkMAC(self, uiMac):
        mac = uiMac.text()
        mac = re.sub('[.:-]', '', mac).lower()
        mac = ''.join(mac.split())
        if len(mac) != 12 or not mac.isalnum():
            valid = False
            uiMac.setStyleSheet(self.TEXT_COLOR_RED)
        else:
            valid = True
            uiMac.setStyleSheet(self.TEXT_COLOR_BLUE)
            mac = ":".join(["%s" % (mac[i:i + 2]) for i in range(0, 12, 2)])
        return valid, mac


from .models import Question, Answer, Option, ActiveQuestion

class QuestionCreateForm:
    def createQuestion(self, text, teacher):
        instance = Question(text=text, teacher=teacher)
        instance.save()
        return instance.id

class ActiveQuestionCreateForm:
    def createActiveQuestion(self, question_id):
        question_instance = Question(pk=question_id)
        instance = ActiveQuestion(question=question_instance)
        instance.save()
        return instance.id

class AnswerCreateForm:
    def createAnswer(self, option_id, student):
        opt_instance = Option.objects.filter(id=option_id)[0]
        question = opt_instance.question
        active_question = ActiveQuestion.objects.filter(question=question)[0]
        instance = Answer(option=opt_instance, student=student,
                          active_question=active_question)
        instance.save()
        return instance.id

class OptionCreateForm:
    def createOption(self, text, question_id):
        question_instance = Question(pk=question_id)
        instance = Option(question=question_instance, text=text)
        instance.save()
        return instance.id


import Common

# e.g. 1cm = $2
referenceLengthAndValues = [
	[1, 2],
	[2, 5],
	[3, 7],
	[4, 8]
]

targetLength = 5

matrix = []
solution = []
numOfRows = len(referenceLengthAndValues)
numOfColumns = targetLength + 1

def computeMatrix():
    for rowIndex in range(0, numOfRows, 1):
        row = []
        matrix.append(row)
        referenceLength = referenceLengthAndValues[rowIndex][0]
        referenceValue = referenceLengthAndValues[rowIndex][1]
        for columnIndex in range(0, numOfColumns, 1):
            currentTotalLength = columnIndex
            if currentTotalLength == 0:
                row.append(0)
            else:
                # LHS candidate for max (previous row value)
                option1 = 0
                if rowIndex > 0:
                    option1 = matrix[rowIndex - 1][columnIndex]

                # RHS candidate for max
                option2 = 0
                if currentTotalLength >= referenceLength:
                    option2 = referenceValue
                # Add the value of remaining maximized length (reuse the sub-problem)
                previousColumnIndex = columnIndex - referenceLength
                if previousColumnIndex > 0:
                    option2 = option2 + matrix[rowIndex][previousColumnIndex]
                # Take max
                maxValue = max(option1, option2)
                row.append(maxValue)

def backtrack():
    row = numOfRows - 1
    column = numOfColumns - 1
    while column >= 0 and row >= 0:
        currentRowValue = matrix[row][column]
        previousRowValue = matrix[row - 1][column] if row - 1 >= 0 else 0
        previousColumnValue = matrix[row][column - 1] if column >= 0 else 0
        if currentRowValue == previousRowValue:
            row = row - 1
        else:
            # pick the current row in solution
            solution.append(referenceLengthAndValues[row])
            # subtract column by current length
            column = column - referenceLengthAndValues[row][0]

print("Target length = {0}".format(targetLength))
Common.printMatrix(referenceLengthAndValues, label="Input matrix", r="first column length", c="second column price")
computeMatrix()
Common.printMatrix(matrix, label="Generated matrix", r="input length and price", c="target length", symbol="$")
backtrack()
Common.printMatrix(solution, label="Solution matrix", r="first column length", c="second column price")


import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello') # deklaracja kolejki

def callback(ch, method, properties, body): # funkcja callbacku służąca do odbioru informacji z kolejki
    print(f'[x] received {body}')

channel.basic_consume(queue='hello',
                      on_message_callback=callback,
                      auto_ack=True)

print('[*] waiting for a new message')

channel.start_consuming()


import os
import kwapi.plugins.api.app as app
import unittest
import tempfile
import time
import json
import socket

class APITestCase(unittest.TestCase):
    def setUp(self):
        self.maxDiff = None
        self.log_file_fd, app.cfg.CONF.log_file = tempfile.mkstemp()
        self.endpoint_fd, probes_endpoint = tempfile.mkstemp()
        app.cfg.CONF.probes_endpoint = ["ipc://" + probes_endpoint]
        my_app = app.make_app()
        self.col = app.collector
        self.app = my_app.test_client()
        self.site = socket.getfqdn().split('.')
        self.site = self.site[1] if len(self.site) >= 2 else self.site[0]

    def tearDown(self):
        self.col.database = {}
        os.close(self.log_file_fd)
        os.unlink(app.cfg.CONF.log_file)
        os.close(self.endpoint_fd)
        os.unlink(app.cfg.CONF.probes_endpoint[0][6:])

    def test_empty_root(self):
        rv = self.app.get('/')
        self.assertEqual("Welcome to Kwapi!", rv.data)

    def test_empty_probes_ids(self):
        rv = self.app.get('/probe-ids/')
        a = {u'probe_ids': []}
        self.assertEqual(a, json.loads(rv.data))

    def test_empty_probes(self):
        rv = self.app.get('/probes/')
        a = {u'probes': {}}
        self.assertEqual(a, json.loads(rv.data))

    def test_empty_probes_unknown(self):
        rv = self.app.get('/probes/foo/')
        a = {}
        self.assertEqual(a, json.loads(rv.data))

    def test_empty_probes_metric_unknown(self):
        rv = self.app.get('/probes/foo/bar/')
        assert "404" in rv.status

    def add_value(self, probe, probes_names, data_type, timestamp, measure, params):
        return self.col.add(probe, probes_names, data_type, timestamp, measure, params)

    def test_probes(self):
        t = int(time.time())
        probe = u"%s.%s" % (self.site, "bar-1")
        self.add_value([probe], [probe], 'power', t, 1, {'type': "power", 'unit': "KW"})
        rv = self.app.get('/probes/')
        a = {u'probes': {
            u'power': {
                probe: dict(timestamp=t, type=u'power', unit=u'KW', value=1)
            }
        }
        }
        b = json.loads(rv.data)
        self.assertEqual(a, b)

    def test_probes_ids(self):
        t = int(time.time())
        probe = u"%s.%s" % (self.site, "bar-1")
        self.add_value([probe], [probe], 'power', t, 1, {'type': "power", 'unit': "KW"})
        rv = self.app.get('/probe-ids/')
        a = {u'probe_ids': [probe]}
        self.assertEqual(a, json.loads(rv.data))

    def test_probes_name(self):
        t = int(time.time())
        probe = u"%s.%s" % (self.site, "bar-1")
        self.add_value([probe], [probe], 'power', t, 1, {'type': "power", 'unit': "KW"})
        rv = self.app.get('/probes/%s.bar-1/' % self.site)
        a = {u'power': {
                probe: dict(timestamp=t, type=u'power', unit=u'KW', value=1)
            }
        }
        b = json.loads(rv.data)
        self.assertEqual(a, b)

    def test_probes_name_metric(self):
        t = int(time.time())
        probe = u"%s.%s" % (self.site, "bar-1")
        self.add_value([probe], [probe], 'power', t, 1, {'type': "power", 'unit': "KW"})
        self.add_value([probe], [probe], 'network_in', t, 1, {'type': "network_in", 'unit': "B"})
        self.add_value([probe], [probe], 'network_out', t, 1, {'type': "network_out", 'unit': "B"})
        rv = self.app.get('/probes/%s.bar-1/power/' % self.site)
        a = {u'power': {
                probe: dict(timestamp=t, type=u'power', unit=u'KW', value=1)
            }
        }
        b = json.loads(rv.data)
        self.assertEqual(a, b)

    def test_inter_switch(self):
        """Test inter-switch links on the following topology
        gw.site [1/1] <=> [1/2] switch.site
        Traffic:
        * from switch to gw: 1B
        * from gw to switch: 2B
        """
        t = int(time.time())
        probe = "%s.%s" % (self.site, "gw-switch")
        switch = "%s.%s.%d-%d" % (self.site, "gw", 1, 1)
        self.add_value(switch, [probe], 'network_in', t, 1,
                       {'type': "network_in", 'unit': "B"})
        self.add_value(switch, [probe], 'network_out', t, 2,
                       {'type': "network_out", 'unit': "B"})
        rv = self.app.get('/probes/%s.gw-switch/network_in/' % self.site)
        a = {u'network_in': {
            u'%s.gw-switch' % self.site: {
                u'timestamp': t,
                u'type': u'network_in',
                u'value': 1,
                u'unit': u'B'}}}
        b = json.loads(rv.data)
        self.assertDictEqual(a, b)
        rv = self.app.get('/probes/%s.gw-switch/network_out/' % self.site)
        a = {u'network_out': {
            u'%s.gw-switch' % self.site: {
                u'timestamp': t,
                u'type': u'network_out',
                u'value': 2,
                u'unit': u'B'}}}
        b = json.loads(rv.data)
        self.assertDictEqual(a, b)

if __name__ == '__main__':
    unittest.main()


from Code.Tools import get_job
from Code.Tools import assign_job
from Code.Tools import get_name
from Code.Tools import run_multi_process
from Code.Tools import write_log
from Code.Tools import update_graph_scale_log

from Config import *
from MaximizeCLC import do_maximize_comb_local_centrality_job

if __name__ == '__main__':

    graph_name_list = [get_name(graph_id) for graph_id in graph_id_list]

    """
    0. Get the scale of network.
    1. scale[0] = |V|, use this information to get max monitor number.
    2. Use |V| and trunc_point_num to get trunc_step.
    3. Instantiate monitor_info_fmt.
    """
    scale_list = update_graph_scale_log(graph_dir, graph_id_list, graph_scale_log_path)
    max_monitor_num_list = [get_max_monitor_num(scale[0]) for scale in scale_list]
    trunc_step_list = [get_step(item) for item in max_monitor_num_list]
    trunc_num_step_method_list = [trunc_num_step_method.format(trunc_point_num, item)
                                  for item in trunc_step_list]

    """
    Instantiate monitor pattern:
    [graph_name_str, monitor_info_fmt, monitor_str]

    """
    monitor_list = [graph_name_list,
                    trunc_num_step_method_list,
                    [monitor_str] * len(graph_name_list)
                    ]

    job_list = get_job(monitor_list, sep, monitor_dir)

    job_assign_res, needed_cpu_num = assign_job(job_list, accessible_cpu_num)

    messages = run_multi_process(do_maximize_comb_local_centrality_job,
                                 needed_cpu_num,
                                 job_assign_res,
                                 is_communication=True)

    if messages:
        write_log(monitor_log_path, messages)


#!/usr/bin/python
import cPickle
from collections import defaultdict
import re

f = open("apps_and_cat_no_dupli_id.txt", "rb")
g = open("dictionnaire_id.pkl", "wb")
dictionnaire = dict()
for line in f:
    line = line.rstrip().split("\t")
    dictionnaire[line[0]] = int(line[1])
cPickle.dump(dictionnaire, g, -1)
f.close()
g.close()


# -*- coding: utf-8 -*-

#14. 先頭からN行を出力
#自然数Nをコマンドライン引数などの手段で受け取り，入力のうち先頭のN行だけを表示せよ．確認にはheadコマンドを用いよ．

import sys

parm = sys.argv
#「sys.argv」に起動時のパラメータが格納されます。
#リストで値が返されますが、最初の値には実行ファイル名が入りますので注意しましょう。
#http://www.python-izm.com/contents/basis/command_line_arguments.shtml

print("渡されたパラメーターを一応確認表示です")
print(parm)
#エラー処理を入れておきます
#http://symfoware.blog68.fc2.com/blog-entry-873.html

#なんと、先日考察した、readlinesがNULLになるのがここで生きます
#行数をとるのと読むのと、二つを回します
try:
    x = int(parm[1])
    f1 = open ("./data/hightemp.txt","r",encoding="utf-8")
    f2 = open ("./data/hightemp.txt","r",encoding="utf-8")
    with f1, f2:
        linen = len(f1.readlines())
        print("全部で" + str(linen) + "行。" + str(parm[1]) + "行まで表示")
        if x <= linen:
            lines = f2.readlines()
            for y in range(0,x): #最初は含む 最後は含まない・・・わかりにくい
                #http://atkonn.blogspot.jp/2008/02/python-python21-range.html
                print(lines[y].replace('\n', ''))
        else:
            print(str(linen) + "までの自然数を入れてください")
except ValueError:
    print ("自然数を入力してください")

#Windows ではコマンドラインで　python ans14k.py 3 で実行します

#　UNIXでは、　head -n N hightemp.txt で確認できます。はい。


# https://www.hackerrank.com/challenges/any-or-all/problem

x = input()
n = input().split()

# Use all to check if every elemet in interable is positive to operator
# Use any to check if one solo element in iterable is positive to operator

def checker(n):
    if all([int(i)>0 for i in n]):
        if any([j == j[::-1] for j in n]): return True
        else: return False
    else: return False
print(checker(n))


from downloadfactory.downloads.miscellaneous.country_api import CountryDownload

class DownloadType(object):
    def __index__(self, name, description, download_type):
        self.name = name
        self.description = description
        self.download_type = download_type

class DownloadFactory(object):
    downloads = dict()

    downloads['country-download'] = CountryDownload

    @staticmethod
    def get_client(name, *args, **kwargs):
        client = DownloadFactory.downloads.get(name.lower(), None)

        if client:
            return client(*args, **kwargs)


from flask import Flask, app, session, redirect, url_for, render_template, request

app = Flask(__name__)
app.secret_key = 'any random string'

@app.route('/')
def index():
    if 'username' in session:
        username = session['username']
        return 'Logged in as ' + username + '
' + "click here to logout"
    return "You are not logged in
click here to login"

@app.route('/login', methods = ['POST', 'GET'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return render_template('session.html')

@app.route('/logout')
def logout():
    # remove the usesrname from session if it is there
    session.pop('username', None)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)


import numpy as np
import redisai as rai
from ml2rt import load_model
model = load_model("../models/spark/pca/spark.onnx")

con = rai.Client()
con.modelset("spark_model", rai.Backend.onnx, rai.Device.cpu, model)
dummydata = np.array(
    [[2.0, 0.0, 3.0, 4.0, 5.0], [4.0, 0.0, 0.0, 6.0, 7.0]],
    dtype=np.float32)
tensor = rai.BlobTensor.from_numpy(dummydata)
con.tensorset("input", tensor)
con.modelrun("spark_model", ["input"], ["output"])
outtensor = con.tensorget("output", as_type=rai.BlobTensor)
print(outtensor.to_numpy())


from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render
from django.views.generic import ListView

from PlayFilms import settings
from PlayFilms.mixins import premium_required, PremiumRequiredMixin
from catalogue.models import Content, Episode

# Create your views here.
from userprofiles.models import History

def mejor_calificados():
    return Content.objects.all().order_by('-score')[0:5]

def cargar_info_usuario(request):
    is_auth = False
    username = None
    if request.user.is_authenticated():
        is_auth = True
        username = request.user.username
    data = {
        'is_auth': is_auth,
        'username': username,
    }
    return data

def buscar(request):
    content = None
    name = None
    es_busqueda = False
    if request.method == 'GET':
        name = request.GET.get('q', None)
        if name is not None:
            print(name)
            es_busqueda = True
            content = Content.objects.filter(
                Q(title__contains=name) | Q(genre__name__startswith=name) | Q(genre__name__endswith=name)).distinct()
    if name is None:
        content = Content.objects.all()
    return {'contenido': content, 'es_busqueda': es_busqueda, 'parametro': name}

@login_required(login_url='/user/login/')
@premium_required
def content_list(request):
    data = cargar_info_usuario(request)
    content = buscar(request)
    return render(request, 'catalogo.html', {'catalogue': content['contenido'],
                                             'data': data,
                                             'es_busqueda': content['es_busqueda'],
                                             'parametro': content['parametro']})

class ContentListView(PremiumRequiredMixin, LoginRequiredMixin, ListView):
    model = Content
    context_object_name = 'catalogue'
    template_name = 'catalogo.html'
    login_url = '/user/login'

    def get_queryset(self):
        order = self.request.GET.get('orden', 'title')
        filtro = int(self.request.GET.get('filtro', 1))
        if filtro == 1:
            queryset = self.model.objects.order_by(order)
        else:
            queryset = self.model.objects.order_by(order).reverse()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(ContentListView, self).get_context_data(**kwargs)
        data = cargar_info_usuario(self.request)
        context['data'] = data
        return context

@login_required(login_url='/user/login/')
@premium_required
def movies_view(request):
    data = cargar_info_usuario(request)
    content = Content.objects.filter(type_of_content=1)
    return render(request, 'catalogo.html', {'catalogue': content, 'data': data})

@login_required(login_url='/user/login/')
@premium_required
def series_view(request):
    data = cargar_info_usuario(request)
    content = Content.objects.filter(type_of_content=2)
    return render(request, 'catalogo.html', {'catalogue': content, 'data': data})

@login_required(login_url='/user/login/')
@premium_required
def film_detail(request, pk):
    film = Content.objects.get(pk=pk)
    data = cargar_info_usuario(request)
    temp_user = request.user.userprofile
    content = mejor_calificados()
    if History.objects.filter(user_profile=temp_user, content=film, is_favorite=True).exists():
        data['fav_submit'] = 'Quitar de favoritos'
        data['class_fav'] = 'btn-danger'
    else:
        data['fav_submit'] = 'Agregar a favoritos'
        data['class_fav'] = 'btn-default'
    try:
        aux_score = History.objects.get(user_profile=temp_user, content=film)
    except History.DoesNotExist:
        aux_score = None

    if aux_score is not None:
        data['mi_puntaje'] = aux_score.score

    return render(request, 'pelicula.html', {'film': film, 'data': data, 'content': content})

@login_required(login_url='/user/login/')
@premium_required
def serie_detail(request, pk):
    serie = Content.objects.get(pk=pk)
    data = cargar_info_usuario(request)
    content = mejor_calificados()
    temp_user = request.user.userprofile
    if History.objects.filter(user_profile=temp_user, content=serie, is_favorite=True).exists():
        data['fav_submit'] = 'Quitar de favoritos'
        data['class_fav'] = 'btn-danger'
    else:
        data['fav_submit'] = 'Agregar a favoritos'
        data['class_fav'] = 'btn-default'

    try:
        aux_score = History.objects.get(user_profile=temp_user, content=serie)
    except History.DoesNotExist:
        aux_score = None

    if aux_score is not None:
        data['mi_puntaje'] = aux_score.score

    return render(request, 'serie.html', {'serie': serie, 'data': data, 'content': content})

@login_required(login_url='/user/login/')
@premium_required
def episode_detail(request, pk):
    episode = Episode.objects.get(pk=pk)
    data = cargar_info_usuario(request)
    content = mejor_calificados()
    return render(request, 'episode.html', {'episode': episode, 'data': data, 'content': content})

def agregar_favorito_view(request):
    data = {'error': 'error'}
    if request.method == 'GET':
        pk = int(request.GET.get('film', None))
        if pk is not None:
            film = Content.objects.get(id=pk)
            temp_user = request.user.userprofile
            if temp_user.favorites.filter(id=pk).exists():
                history = History.objects.get(user_profile=temp_user, content=film)
                history.is_favorite = not history.is_favorite
                data = {'favorito': history.is_favorite}
                history.save()
            else:
                data = {'favorito': True}
                history = History.objects.create(user_profile=temp_user, content=film, is_favorite=True)
                history.save()
    return JsonResponse(data)

def puntuar_view(request):
    data = {'ok': False}
    if request.method == 'GET':
        pk = int(request.GET.get('film', None))
        score = int(request.GET.get('calificacion', None))
        if (pk and score) is not None:
            film = Content.objects.get(id=pk)
            temp_user = request.user.userprofile
            if temp_user.favorites.filter(id=pk).exists():
                history = History.objects.get(user_profile=temp_user, content=film)
                history.score = score
                history.save()
            else:
                history = History.objects.create(user_profile=temp_user, content=film, score=score)
                history.save()
            data['puntaje'] = Content.objects.get(id=pk).score
            data['ok'] = True
    return JsonResponse(data)

class GenreListView(PremiumRequiredMixin, LoginRequiredMixin, ListView):
    model = Content
    context_object_name = 'catalogue'
    template_name = 'catalogo.html'
    login_url = '/user/login'

    def get_queryset(self):
        if self.kwargs.get('genre'):
            queryset = self.model.objects.filter(genre__name__contains=self.kwargs['genre'])
        else:
            queryset = super(GenreListView, self).get_queryset()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(GenreListView, self).get_context_data(**kwargs)
        data = cargar_info_usuario(self.request)
        context['data'] = data
        return context


# -*- coding: utf-8 -*-
"""
Created on Sun Aug  9 16:08:51 2020

@author: Pablo Saldarriaga
"""
import os
import json
import time
import random
import funciones
import numpy as np
import pandas as pd
import datetime as dt
from tqdm import tqdm
import sklearn.metrics as metrics
# %%
# Define el archivo en el que se guararan los logs del codigo
import logging
from logging.handlers import RotatingFileHandler

file_name = "Algoritmo_genetico"
logger = logging.getLogger()
dir_log = f"../logs/{file_name}.log"

# Crea la carpeta de logs
if not os.path.isdir("../logs"):
    os.makedirs("../logs", exist_ok=True)

handler = RotatingFileHandler(dir_log, maxBytes=2000000, backupCount=10)
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(process)d - %(name)s - %(levelname)s - %(message)s",
    handlers=[handler],
)
# %%
# Esta funcion obtiene la metrica para cada uno de los modelos entrenados
# con las diferentes soluciones

def evaluar_fitness_poblacion(X, Y, X_val, Y_val, population, k=3):

    poblacion = population.copy()
    fitness = []
    best_score = 0
    best_mod = None
    for i in range(len(poblacion)):

        if not "fitness" in poblacion[i].keys():
            _, score = funciones.entrenar_NN(X, Y, X_val, Y_val, k, poblacion[i])
            fitness.append(score)
            poblacion[i]["fitness"] = score
        else:
            score = poblacion[i]["fitness"]
            fitness.append(score)

        if score > best_score:
            best_score = score
            best_mod = poblacion[i]

    fitness = np.array(fitness)

    return poblacion, fitness, best_mod, best_score

def seleccion(poblacion):

    ### Seleccion por metodo de torneo
    ### Definicion de cantidad de individuos a participar en el torneo
    k = 5
    fitness = []
    participantes = random.sample(poblacion, k)

    for i in range(k):
        fitness.append(participantes[i]['fitness'])

    indices_best2worst = np.argsort(fitness)[::-1]

    ### Se seleccionan los dos mejores individuos acorde a su fitness
    padre_1 = participantes[indices_best2worst[0]]
    padre_2 = participantes[indices_best2worst[1]]

    return padre_1, padre_2

def get_bestPoblacion(fitness, poblacion, cant_seleccion):

    indices_best2worst = np.argsort(fitness)[::-1]
    # fitness_best2worst = fitness[indices_best2worst]

    poblacion_best2worst = []
    for ind in indices_best2worst:
        poblacion_best2worst.append(poblacion[ind])

    best_pop = poblacion_best2worst[:cant_seleccion]

    return best_pop

def crossover(X, Y, X_val, Y_val, k, padre_1a, padre_2a, bin_cont = False):
    ### Cruce por dos puntos

    padre_1 = padre_1a.copy()
    padre_2 = padre_2a.copy()

    array_p1 = funciones.sol2array(padre_1, bin_cont)
    array_p2 = funciones.sol2array(padre_2, bin_cont)

    array_cruce1 = list(np.zeros(len(array_p1)))
    array_cruce2 = list(np.zeros(len(array_p1)))

    ### Encuentro los dos puntos en los cuales se hace el cruce
    aa = -1
    bb = -1

    while (aa == bb) or (abs(aa - bb) < 2):
        aa = random.randint(1, len(array_p1) - 2)
        bb = random.randint(1, len(array_p1) - 2)

    a = min(aa, bb)
    b = max(aa, bb)

    for i in range(len(array_p1)):

        if i < a:
            array_cruce1[i] = array_p1[i]
            array_cruce2[i] = array_p2[i]

        elif i < b:
            array_cruce1[i] = array_p2[i]
            array_cruce2[i] = array_p1[i]

        else:
            array_cruce1[i] = array_p1[i]
            array_cruce2[i] = array_p2[i]

    hijo_1 = funciones.array2sol(array_cruce1, bin_cont)
    hijo_2 = funciones.array2sol(array_cruce2, bin_cont)

    _, score_h1 = funciones.entrenar_NN(X, Y, X_val, Y_val, k, hijo_1)
    _, score_h2 = funciones.entrenar_NN(X, Y, X_val, Y_val, k, hijo_2)

    if score_h1 >= score_h2:
        best_h = hijo_1
        best_h['fitness'] = score_h1
    else:
        best_h = hijo_2
        best_h['fitness'] = score_h2

    return best_h

def mutacion(hijo_a, min_lay, max_lay, min_neu, max_neu, min_alpha, max_alpha,
             min_lr, max_lr, escenario=1,bin_cont = False):

    hijo = hijo_a.copy()
    ops = ["layer_sizes", "activation", "alpha", "learning_rate_init"]
    if escenario == 1:
        ### Este escenario selecciona de forma aleatoria uno de los 4 hiperparametros a variar
        gen = random.choice(ops)

        if gen == "layer_sizes":

            layer = funciones.change_layer(hijo, min_lay, max_lay, min_neu, max_neu)

            hijo["layer_sizes"] = tuple(layer)

        elif gen == "activation":

            hijo["activation"] = funciones.change_activation(hijo)

        elif gen == "alpha":
            hijo["alpha"], hijo["alpha_bin"] = funciones.change_alpha(hijo, min_alpha, max_alpha, bin_cont = bin_cont)

        else:
            hijo["learning_rate_init"], hijo["learning_rate_init_bin"] = funciones.change_learningRate(hijo, min_lr, max_lr, bin_cont = bin_cont)
    elif escenario == 2:
        ### Este escenario le asigna una probabilidad de mutacion a cada hiperparametro
        probabilidades = [0.7, 0.6, 0.4, 0.4]

        ### layers
        if random.random() < probabilidades[0]:

            layer = funciones.change_layer(hijo, min_lay, max_lay, min_neu, max_neu)

            hijo["layer_sizes"] = tuple(layer)

        ### activation
        if random.random() < probabilidades[1]:

            hijo["activation"] = funciones.change_activation(hijo)

        ### alpha
        if random.random() < probabilidades[2]:
            hijo["alpha"], hijo["alpha_bin"] = funciones.change_alpha(hijo, min_alpha, max_alpha, bin_cont = bin_cont)

        ### Learning rate
        if random.random() < probabilidades[3]:
            hijo["learning_rate_init"], hijo["learning_rate_init_bin"] = funciones.change_learningRate(hijo, min_lr, max_lr, bin_cont = bin_cont)

    elif escenario == 3:
        ### Este escenario selecciona de forma aleatoria uno de los 4 hiperparametros a variar.
        ### la diferencia con el escenario 1, es que  se le asignan pesos a cada uno de los hiperparametros
        ### por lo que la seleccion no es uniforme

        gen_n = np.random.choice([0, 1, 2, 3], 1, p=[0.6, 0.2, 0.1, 0.1], replace=False)
        gen = ops[gen_n[0]]
        if gen == "layer_sizes":

            layer = funciones.change_layer(hijo, min_lay, max_lay, min_neu, max_neu)

            hijo["layer_sizes"] = tuple(layer)

        elif gen == "activation":

            hijo["activation"] = funciones.change_activation(hijo)

        elif gen == "alpha":
            hijo["alpha"], hijo["alpha_bin"] = funciones.change_alpha(hijo, min_alpha, max_alpha, bin_cont = bin_cont)

        else:
            hijo["learning_rate_init"], hijo["learning_rate_init_bin"] = funciones.change_learningRate(hijo, min_lr, max_lr, bin_cont = bin_cont)

    return hijo

def myFunc(e):
  return e[0]

def update_poblacion(X_train, Y_train, X_val, Y_val, poblacion_prev, fitness, hijos_prev, k, bin_cont = False):

    poblacion = poblacion_prev.copy()
    hijos = hijos_prev.copy()

    N = len(poblacion)

    ### Agrego tanto hijos como individuos de la poblacion a una sola lista
    res = []
    for ind in poblacion:
        res.append([ind['fitness'].copy(),ind.copy()])

    for hijo in hijos:
        res.append([hijo['fitness'].copy(),hijo.copy()])

    ### organizo los individuos por su fitness
    res.sort(reverse = True, key=myFunc)

    N2 = len(res)
    ### inicializo variables para comenzar a actualizar los individuos de la
    ### poblacion buscando que los individuos sean lo mas diferente posible
    ids_check = []
    new_population = []

    ### Agrego a la nueva poblacion el mejor individuo
    ids_check.append(0)
    new_population.append(res[0][1].copy())

    ### Calculo la matriz de distancias de los individuos de la poblacion
    mat_dist = np.zeros(shape = (N2,N2))

    for i in range(N2):
        for j in range(N2):
            sol1 = res[i][1].copy()
            sol2 = res[j][1].copy()

            sol1_arr = funciones.sol2array(sol1, bin_cont = bin_cont)
            sol2_arr = funciones.sol2array(sol2, bin_cont = bin_cont)

            sol1_arr_z = np.array(funciones.estandatizar_sol(sol1_arr))
            sol2_arr_z = np.array(funciones.estandatizar_sol(sol2_arr))

            mat_dist[i,j] = np.linalg.norm(sol1_arr_z-sol2_arr_z)

    ### comenzamos a actualizar la poblacion con base en la matriz de distancias
    for _ in range(1,N):

        ### inicializo diccionario
        dic_cercanos = {}
        for k in range(N2):
            dic_cercanos[k] = 0

        for index in ids_check:
            mas_cercanos = np.argsort(mat_dist[index,:])[1:]

            for v in range(len(mas_cercanos)):
                if mas_cercanos[v] not in ids_check:
                    dic_cercanos[mas_cercanos[v]] = dic_cercanos[mas_cercanos[v]] + v

        max_sum = -1
        a_agregar = 0
        for key in dic_cercanos.keys():
            if max_sum < dic_cercanos[key]:
                max_sum = dic_cercanos[key]
                a_agregar = key

        ids_check.append(a_agregar)
        new_population.append(res[a_agregar][1].copy())

    return new_population, fitness, new_population[0].copy(), new_population[0]['fitness'].copy()

def busqueda_exahustiva(X_train, Y_train, X_val, Y_val, k, best_mod):
    logger.info('Inicia busqueda exahustiva')
    best_sol = best_mod.copy()

    sol_aux = best_mod.copy()
    for f_activation in ["identity", "logistic"]:

        for n_layers in range(1,5):

            if n_layers <= len(sol_aux['layer_sizes']):
                new_layers = sol_aux['layer_sizes'][:n_layers]
            else:
                ones = []
                cant_ones = n_layers - len(sol_aux['layer_sizes'])
                for _ in range(cant_ones):
                    ones.append(1)

                new_layers = tuple([*list(sol_aux['layer_sizes']), *ones])

            new_sol = best_mod.copy()
            new_sol['layer_sizes'] = new_layers
            new_sol['activation'] = f_activation

            _, score = funciones.entrenar_NN(X_train, Y_train, X_val, Y_val, k, new_sol)

            new_sol["fitness"] = score

            if best_sol['fitness']< new_sol["fitness"]:
                best_sol = new_sol.copy()
                logger.info(f'Mejora la solucion con la busqueda exahustiva: {best_sol}')

    return best_sol

def busqueda_local(X_train, Y_train, X_val, Y_val, k, min_alpha, max_alpha,
                                min_lr, max_lr, best_mod, vec_size =5,
                                bin_cont = False):
    ### inicializo soluciones a considerar
    best_sol = best_mod.copy()

    lr = best_sol['learning_rate_init']
    alpha = best_sol['alpha']

    ### discretizo los posibles valores de learning_rate y alpha
    step_size = 20
    h_lr = (max_lr-min_lr)/step_size
    h_alpha = (max_alpha-min_alpha)/step_size

    lr_list = []
    alpha_list = []
    for i in range(step_size+1):
        lr_list.append(min_lr+h_lr*i)
        alpha_list.append(min_alpha+h_alpha*i)

    lr_list = np.array(lr_list)
    alpha_list = np.array(alpha_list)

    ### obtengo binarizacion de las dos variables continuas
    bin_lr = funciones.binarize_parameter(lr, lr_list)
    bin_alpha = funciones.binarize_parameter(alpha, alpha_list)

    if bin_cont:
        logger.info('Entra a bin cont')
        ### creacion del vecindario.
        vecindario = funciones.create_neighborhood(best_mod['learning_rate_init_bin'], best_mod['alpha_bin'], vec_size, bin_cont)
        ### iterar sobre el vecindario
        for vecino in vecindario:
            lr_vecino = vecino[0]
            alpha_vecino = vecino[1]

            new_sol = best_mod.copy()
            new_sol['learning_rate_init_bin'] = lr_vecino
            new_sol['learning_rate_init'] = funciones.bin2cont(lr_vecino, 1)
            new_sol['alpha_bin'] = alpha_vecino
            new_sol['alpha'] = funciones.bin2cont(alpha_vecino, 10)

            ### obtengo el fitness del vecino en la iteracion
            _, score = funciones.entrenar_NN(X_train, Y_train, X_val, Y_val, k, new_sol)
            new_sol["fitness"] = score

            ### conserva el primer vecino que mejore la FO
            if best_sol['fitness']< new_sol["fitness"]:
                best_sol = new_sol.copy()
                logger.info(f'Mejora la solucion con la busqueda local: {best_sol}')
                print('Mejora')
                return best_sol
    else:
        vecindario = funciones.create_neighborhood(bin_lr, bin_alpha, vec_size, bin_cont)
        ### iterar sobre el vecindario
        for vecino in vecindario:
            lr_vecino = vecino[0]
            alpha_vecino = vecino[1]

            new_sol = best_mod.copy()
            new_sol['learning_rate_init'] = lr_list[lr_vecino==1][0]
            new_sol['alpha'] = alpha_list[alpha_vecino==1][0]

            ### obtengo el fitness del vecino en la iteracion
            _, score = funciones.entrenar_NN(X_train, Y_train, X_val, Y_val, k, new_sol)
            new_sol["fitness"] = score

            ### conserva el primer vecino que mejore la FO
            if best_sol['fitness']< new_sol["fitness"]:
                best_sol = new_sol.copy()
                logger.info(f'Mejora la solucion con la busqueda local: {best_sol}')
                print('Mejora')
                return best_sol

    logger.info('NO HAY MEJORA EN EL VECINDARIO')
    return best_sol

# %%

def AG(X_train,
       Y_train,
       X_val,
       Y_val,
       k=3,
       num_generacion=5,
       population_size=10,
       prob_seleccion=0.5,
       prob_mutacion=0.2,
       escenario=1,
       iteracion = 0,
       desc = 'run',
       BH = False,
       LS = False,
       bin_cont = False,
       vec_size = 5,
       threshold_roc = 0.77):

    time_ini = time.time()

    logger.info("***" * 20)
    logger.info("INICIA ALGORITMO GENETICO")
    logger.info("***" * 20)

    # Fijar semillas
    SEED = 42
    random.seed(SEED)
    np.random.seed(SEED)

    # Definicion de parametros del AG
    cant_seleccion = int(population_size * prob_seleccion)

    # Creacion de la poblacion
    min_lay = 1
    max_lay = 4
    min_neu = 1
    max_neu = 128
    min_alpha = 0.0001
    max_alpha = 10
    min_lr = 0.001
    max_lr = 1

    historia_score = []

    poblacion = funciones.crear_soluciones(
        population_size,
        min_lay,
        max_lay,
        min_neu,
        max_neu,
        min_alpha,
        max_alpha,
        min_lr,
        max_lr,
        bin_cont = bin_cont)

    poblacion, fitness, best_mod, best_score = evaluar_fitness_poblacion(X_train, Y_train, X_val, Y_val, poblacion, k=k)

    historia_score.append(best_score)
    last_sol = ''
    for i in tqdm(range(num_generacion)):
        logger.info(f'generacion {i} de {num_generacion}')
        hijos = []
        for j in range(population_size - cant_seleccion):

            padre_1, padre_2 = seleccion(poblacion)
            hijo = crossover(X_train, Y_train, X_val, Y_val, k, padre_1, padre_2, bin_cont = bin_cont)

            if random.random() < prob_mutacion:
                ### solamente se va a mutar un hijo si es malo, de lo contrario
                ### no se muta
                if hijo['fitness']< threshold_roc:
                    logger.info(f'MUTA HIJO NO TAN BUENO')
                    hijo = mutacion(hijo,
                                    min_lay,
                                    max_lay,
                                    min_neu,
                                    max_neu,
                                    min_alpha,
                                    max_alpha,
                                    min_lr,
                                    max_lr,
                                    escenario=escenario,
                                    bin_cont = bin_cont)

            hijos.append(hijo)

        poblacion, fitness, mod, score = update_poblacion(X_train,
                                                          Y_train,
                                                          X_val,
                                                          Y_val,
                                                          poblacion,
                                                          fitness,
                                                          hijos,
                                                          k=k)

        if score > best_score:
            best_score = score
            best_mod = mod.copy()

        ### BUSQUEDA EXAHUSTIVA
        if BH:
            if not last_sol==str(best_mod):
                logger.info(f'Inicia busqueda exahustiva en la generacion al final de la generacion {i}')
                ### consideramos una busqueda exahustiva en la mejor solucion encontrada
                best_sol = busqueda_exahustiva(X_train, Y_train, X_val, Y_val, k, best_mod)

                last_sol = str(best_mod.copy())
                last_mod = best_mod.copy()
                ### actualiza la mejor solucion enconrada
                if best_sol['fitness'] > best_score:

                    logger.info('Encuentra mejor solucion en busqueda exahustiva')
                    best_score = best_sol['fitness']
                    best_mod = best_sol.copy()

                    logger.info('Agrega a la poblacion el mejor individuo de la BH')
                    ### ingresamos a la poblacion el mejor individuo encontrado
                    if not best_mod in poblacion:
                        logger.info(f'AGREGADO:{best_mod}')
                        print(f'AGREGADO:{best_mod}')
                        poblacion[poblacion.index(last_mod)] = best_mod.copy()

            else:
                logger.info(f"YA SE EVALUO LA SOLUCION")
                logger.info(f"ANTES: {last_mod}")
                logger.info(f"AHORA: {best_mod}")
        ### BUSQUEDA LOCAL
        if LS:
            if not last_sol==str(best_mod):
                logger.info(f'Inicia busqueda local en la generacion al final de la generacion {i}')
                ### consideramos una busqueda exahustiva en la mejor solucion encontrada
                best_sol = busqueda_local(X_train, Y_train, X_val, Y_val, k,
                                          min_alpha, max_alpha, min_lr, max_lr,
                                          best_mod, vec_size = vec_size, bin_cont = bin_cont)

                last_sol = str(best_mod.copy())
                last_mod = best_mod.copy()
                ### actualiza la mejor solucion enconrada
                if best_sol['fitness'] > best_score:

                    logger.info('Encuentra mejor solucion en busqueda local')
                    best_score = best_sol['fitness']
                    best_mod = best_sol.copy()

                    logger.info('Agrega a la poblacion el mejor individuo de LS')
                    if not best_mod in poblacion:
                        logger.info(f'AGREGADO:{best_mod}')
                        print(f'AGREGADO:{best_mod}')
                        ### ingresamos a la poblacion el mejor individuo encontrado
                        poblacion[poblacion.index(last_mod)] = best_mod.copy()
    #                    poblacion, fitness, mod, score = update_poblacion(X_train,
    #                                                                      Y_train,
    #                                                                      X_val,
    #                                                                      Y_val,
    #                                                                      poblacion,
    #                                                                      fitness,
    #                                                                      [best_mod],
    #                                                                      k=k)
            else:
                logger.info(f"YA SE EVALUO LA SOLUCION")
                logger.info(f"ANTES: {last_mod}")
                logger.info(f"AHORA: {best_mod}")

        historia_score.append(best_score)
        logger.info(f'Historia: {historia_score}')

    bes_mod_entrenado, _ = funciones.entrenar_NN(X_train, Y_train, X_val, Y_val, k, best_mod)

    time_exec = time.time() - time_ini

    ### Guardo resultados
    if bin_cont:
        best_mod['learning_rate_init_bin'] = str(list(best_mod['learning_rate_init_bin']))
        best_mod['alpha_bin'] = str(list(best_mod['alpha_bin']))
    else:
        best_mod['learning_rate_init_bin'] = str([])
        best_mod['alpha_bin'] = str([])

    res = {
        'population_size': population_size,
        'num_generaciones': num_generacion,
        'prob_selecicon': prob_seleccion,
        'prob_mutacion': prob_mutacion,
        'best_score': best_score,
        'best_mod': best_mod,
        'execution_time_mins': time_exec,
        'history': historia_score
    }

    now = dt.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
    with open(f'../data/output/output_{escenario}_{iteracion}_{now}_{desc}.json', 'w') as json_file:
        json.dump(res, json_file)

    logger.info("***" * 20)
    logger.info(f"FIN ALGORITMO GENETICO: tiempo {round(time_exec,4)} mins")
    logger.info("***" * 20)

    logger.info("La mejor solucion fue")
    logger.info(str(best_mod))

    return best_mod, best_score, bes_mod_entrenado, historia_score

## %%
#if __name__ == "__main__":
#
#    logger.info("CARGA DE DATOS")
#    # Carga la informacion para entrenar el modelo
#    train = pd.read_csv("../data/train_z.csv").drop(columns=["TW", "BARRIO"])
#    validation = pd.read_csv("../data/validation_z.csv").drop(columns=["TW", "BARRIO"])
#    test = pd.read_csv("../data/test_z.csv").drop(columns=["TW", "BARRIO"])
#
#    # Divide conjunto de datos en entrenamiento y en prueba
#    X_train = train.drop(columns="Accidente")
#    Y_train = train["Accidente"]
#
#    X_val = validation.drop(columns="Accidente")
#    Y_val = validation["Accidente"]
#
#    X_test = test.drop(columns="Accidente")
#    Y_test = test["Accidente"]
#
#    # Realiza la ejecucion del algoritmo genetico
#    best_mod, best_score, bes_mod_entrenado, historia_score = AG(X_train,
#                                                                  Y_train,
#                                                                  X_val,
#                                                                  Y_val,
#                                                                  num_generacion=5,
#                                                                  population_size=10,
#                                                                  prob_seleccion=0.5,
#                                                                  prob_mutacion=0.2,
#                                                                  escenario=1,
#                                                                  BH = False,
#                                                                  LS = True,
#                                                                  bin_cont = True)
#
#    # realiza las predicciones en el conjutno de prueba
#    proba_test = bes_mod_entrenado.predict_proba(X_test)[:, 1]
#    preds_test = bes_mod_entrenado.predict(X_test)
#
#    # Calcula las metricas del modelo en el conjunto de prueba
#    PR_auc = funciones.precision_recall_auc_score(Y_test, proba_test)
#    ROC_auc = metrics.roc_auc_score(Y_test, proba_test)
#
#    bAccuracy = metrics.balanced_accuracy_score(Y_test, preds_test)
#    precision = metrics.precision_score(Y_test, preds_test)
#    recall = metrics.recall_score(Y_test, preds_test)
#    fscore = metrics.f1_score(Y_test, preds_test)
#
#    logger.info("Desempeño en el conjunto de prueba:")
#    logger.info(f"PR-AUC:{PR_auc}")
#    logger.info(f"ROC-AUC:{ROC_auc}")
#    logger.info(f"Balanced accuracy:{bAccuracy}")
#    logger.info(f"Precision:{precision}")
#    logger.info(f"Recall:{recall}")
#    logger.info(f"F1 Score:{fscore}")

# %%


import json
import time
from datetime import datetime

from django.core.exceptions import ValidationError
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt

from crtool.models import CurriculumReviewSession

@csrf_exempt
def create_review(request):
    if request.method == 'POST':
        fd = json.loads(request.body.decode("utf-8"))

        title = fd['tdp-crt_title'] if 'tdp-crt_title' in fd else ''
        pub_date = fd['tdp-crt_pubdate'] if 'tdp-crt_pubdate' in fd else ''
        grade_range = fd['tdp-crt_grade'] if 'tdp-crt_grade' in fd else ''
        pass_code = fd['tdp-crt_pass_code'] \
            if 'tdp-crt_pass_code' in fd else ''
        review_id = CurriculumReviewSession.id_generator()
        last_updated = timezone.now()

        if not title or not grade_range:
            return HttpResponse(status=400)
        data = {
            'id': str(review_id),
            'pass_code': pass_code,
            'last_updated': str(datetime.isoformat(last_updated)),
            'curriculumTitle': title,
            'publicationDate': pub_date,
            'gradeRange': grade_range
        }

        review = CurriculumReviewSession.objects.create(
            id=review_id,
            pass_code=pass_code,
            last_updated=last_updated,
            data=data
        )

        if review:
            return JsonResponse(review.data)

    return HttpResponse(status=400)

@csrf_exempt
def get_review(request):
    data = {}
    if request.method == 'POST':
        review_id = request.POST.get('token')
        try:
            review = CurriculumReviewSession.objects.get(id=review_id)
            if review:
                data = review.data
        except (
            CurriculumReviewSession.DoesNotExist,
            ValueError,
            ValidationError
        ):
            return HttpResponse(status=404)
        return JsonResponse(data)
    return HttpResponse(status=404)

@csrf_exempt
def continue_review(request):
    if request.method == 'POST':
        review_id = request.POST.get('access_code')
        # Critical pause to prevent brute-forcing the shorter pass_code values
        time.sleep(1)
        try:
            review = CurriculumReviewSession.objects.get(id=review_id)
            if review:
                return HttpResponseRedirect('../tool/#id=' + review.id)
        except (
            CurriculumReviewSession.DoesNotExist,
            ValueError,
            ValidationError
        ):
            return HttpResponse(status=404)
    return HttpResponse(status=404)

@csrf_exempt
def update_review(request):
    if request.method == 'POST':
        data = json.loads(request.body.decode("utf-8"))
        if "id" in data:
            try:
                review = CurriculumReviewSession.objects.get(id=data["id"])
                if review:
                    # Update last_updated date
                    last_updated = timezone.now()
                    iso_last_updated = str(datetime.isoformat(last_updated))
                    data['last_updated'] = iso_last_updated
                    review.data = data
                    review.last_updated = last_updated
                    review.save()
                    return JsonResponse(review.data)
            except (
                CurriculumReviewSession.DoesNotExist,
                ValueError,
                ValidationError
            ):
                return HttpResponse(status=404)

    return HttpResponse(status=404)


from __future__ import division
import keras
import os
import sys
import numpy as np
import scipy.io as sio

from keras import optimizers
from keras import initializers
from keras.models import load_model
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Permute, Flatten, Dense, BatchNormalization, Activation, Dropout

def trainOnMatfile(matfile,modelfile,windowSize,targetdelays):
	if not os.path.isfile(modelfile):
		x_train,y_train,x_val,y_val,x_test,x_trainnc = load_traindata(matfile,windowSize,targetdelays)
		numChannels = x_train.shape[2];
		model = train(modelfile,x_train,y_train,x_val,y_val)

		pred = classify(model,x_test)
		np.savetxt(matfile[:-4]+"_train_pred.csv", pred, delimiter=",")
		pred = classify(model,x_trainnc)
		np.savetxt(matfile[:-4]+"_trainnc_pred.csv", pred, delimiter=",")
	else:
		numChannels = get_channels(matfile)
		model = load_model(modelfile)
	return model,numChannels

def train(filename,x_train,y_train,x_val,y_val):
	if not os.path.isfile(filename):
		# model parameter
		lr = 0.001
		batchsize = 256
		epochs = 25

		model = construct_model(x_train.shape[1],x_train.shape[2])
		adam = keras.optimizers.Adam(lr=lr)
		model.compile(loss='categorical_crossentropy',optimizer=adam,metrics=['accuracy'])
		history = model.fit(x_train, y_train, batch_size=batchsize, epochs=epochs, validation_data=(x_val, y_val), callbacks = [keras.callbacks.ModelCheckpoint(filename, monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=False, mode='auto', period=1)])

	model = load_model(filename)

	return model;

# input: keras model, and test data (windowSize x Channels)
def classify(model,x_test):
	x_test = x_test.reshape(-1,x_test.shape[1],x_test.shape[2],1)
	pred = model.predict(x_test)
	return pred[:,0];

def get_channels(filename):
	print('Load mat file')
	mat_contents = sio.loadmat(filename)
	usedchannels = np.array(mat_contents['usedchannels'])[0]
	numChannels = usedchannels.size
	return numChannels

def load_traindata(filename,windowSize,targetdelays):
	print('Load mat file')
	mat_contents = sio.loadmat(filename)

	# extract matlab struct
	train_data_x = np.array(mat_contents['train_data_x'])
	train_data_y = np.array(mat_contents['train_data_y'])
	trainnc_data_x = np.array(mat_contents['trainnc_data_x'])
	trainnc_data_y = np.array(mat_contents['trainnc_data_y'])
	usedchannels = np.array(mat_contents['usedchannels'])[0]
	targetdelays = np.array(targetdelays)

	print('Splitting train data to windows using the following channels')
	print(usedchannels)
	data_x_train = []
	data_y_train = []
	data_x_test = []
	for ii in range(train_data_x.shape[0]):
		realtarget = ii % 32
		targetshift = targetdelays[realtarget]
		trialdata = train_data_x[ii,usedchannels-1,:].squeeze().transpose()
		trialdatashifted = np.roll(trialdata,-targetshift,axis=0)

		data_x_timelag = np.zeros((trialdata.shape[0],windowSize,len(usedchannels)))
		data_x_timelagshifted = np.zeros((trialdatashifted.shape[0],windowSize,len(usedchannels)))
		for t in range(windowSize):
			data_x_timelag[:,t,:] = np.roll(trialdata,-t,axis=0)
			data_x_timelagshifted[:,t,:] = np.roll(trialdatashifted,-t,axis=0)

		data_x_timelag = data_x_timelag[0:(data_x_timelag.shape[0]-windowSize),:,:]
		data_x_timelagshifted = data_x_timelagshifted[0:(data_x_timelagshifted.shape[0]-windowSize),:,:]
		bitdata = train_data_y[ii,realtarget,:].squeeze().transpose()
		for t in range(data_x_timelag.shape[0]):
			data_x_train.append(data_x_timelagshifted[t,:,:].squeeze())
			data_x_test.append(data_x_timelag[t,:,:].squeeze())
			data_y_train.append([bitdata[t],abs(bitdata[t]-1)])

	# split train and validation set
	(x_train,y_train,x_val,y_val) = split_data(np.array(data_x_train),np.array(data_y_train))
	data_x_test = np.array(data_x_test)

	data_x_trainnc = []
	for ii in range(trainnc_data_x.shape[0]):
		realtarget = ii % 32
		targetshift = targetdelays[realtarget]
		trialdata = trainnc_data_x[ii,usedchannels-1,:].squeeze().transpose()

		data_x_timelag = np.zeros((trialdata.shape[0],windowSize,len(usedchannels)))
		for t in range(windowSize):
			data_x_timelag[:,t,:] = np.roll(trialdata,-t,axis=0)

		data_x_timelag = data_x_timelag[0:(data_x_timelag.shape[0]-windowSize),:,:]
		for t in range(data_x_timelag.shape[0]):
			data_x_trainnc.append(data_x_timelag[t,:,:].squeeze())

	data_x_trainnc = np.array(data_x_trainnc)

	print('done.')

	return x_train,y_train,x_val,y_val,data_x_test,data_x_trainnc

def split_data(x_train,y_train):
	print ('Splitting train data to train set and validation set')
	x_train = x_train.reshape(-1,x_train.shape[1],x_train.shape[2],1)
	y_train = y_train.reshape(-1,2)
	# Split data to train and validation set
	if (len(x_train) % 2) != 0:
		x_train = x_train[:-1]
		y_train = y_train[:-1]
	x_split = np.split(x_train, 2)
	y_split = np.split(y_train, 2)
	# train set
	x_train = x_split[0]
	y_train = y_split[0]
	# validation set
	x_val = x_split[1]
	y_val = y_split[1]
	print ('done.')

	return x_train,y_train,x_val,y_val

def construct_model(windowSize,numChannels):
	# construct sequential model
	model = Sequential()
	# permute input so that it is as in EEG Net paper
	model.add(Permute((3,2,1), input_shape=(windowSize,numChannels,1)))
	# layer1
	model.add(Conv2D(16, kernel_size=(numChannels, 1), padding='valid', strides=(1, 1), data_format='channels_first', activation='relu'))
	model.add(BatchNormalization(axis=1, scale=False, center=False))
	#model.add(Permute((2,1,3)))
	model.add(Activation('relu'))
	model.add(MaxPooling2D(pool_size=(2, 2),strides=(2, 2),padding='same'))
	# layer2
	model.add(Conv2D(8,kernel_size=(1, 64),data_format='channels_first',padding='same'))
	model.add(BatchNormalization(axis=1,scale=False, center=False))
	model.add(Activation('relu'))
	model.add(MaxPooling2D(pool_size=(2, 2),strides=(2, 2),padding='same'))
	model.add(Dropout(0.5))
	# layer3
	model.add(Conv2D(4,kernel_size=(5, 5),data_format='channels_first',padding='same'))
	model.add(BatchNormalization(axis=1,scale=False,center=False))
	model.add(Activation('relu'))
	model.add(MaxPooling2D(pool_size=(2, 2), data_format='channels_first',padding='same'))
	model.add(Dropout(0.5))
	# layer4
	model.add(Flatten())
	model.add(Dense(1024, activation='relu'))
	model.add(Dropout(0.5))
	# layer5
	model.add(Dense(2, activation='softmax'))
	#print(model.summary())
	return model


import pandas as pd
from pandas.plotting import scatter_matrix
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, date2num, WeekdayLocator, DayLocator, MONDAY
from mpl_finance import candlestick_ohlc
import datetime
import seaborn as sns
import quandl

start = datetime.datetime(2012, 1, 1)
end = datetime.datetime(2018,1,1)

# Acquire the Data (Tesla, Ford, GM)
tesla = quandl.get("WIKI/TSLA", start_date = start, end_date = end)
ford = quandl.get("WIKI/F", start_date = start, end_date = end)
gm = quandl.get("WIKI/GM", start_date = start, end_date = end)

# Convert to CSV
tesla.to_csv('TSLA.csv')
ford.to_csv('FORD.csv')
gm.to_csv('GM.csv')

# Visualize the Data

# Plot Open Values
tesla['Open'].plot(label='Tesla',figsize=(16,8),title='Open Price')
gm['Open'].plot(label='GM')
ford['Open'].plot(label='Ford')
plt.legend()
plt.show()

# Plot Volume Each Day
tesla['Volume'].plot(label='Tesla', figsize=(16,8),title='Volume')
gm['Volume'].plot(label='GM')
ford['Volume'].plot(label='Ford')
plt.legend()
plt.show()

# Add "Total Traded" ("Open" price * "Volume")
tesla['Total Traded'] = tesla['Open']*tesla['Volume']
ford['Total Traded'] = ford['Open']*ford['Volume']
gm['Total Traded'] = gm['Open']*gm['Volume']

# Plot Total Traded
tesla['Total Traded'].plot(label='Tesla',figsize=(16,8))
gm['Total Traded'].plot(label='GM')
ford['Total Traded'].plot(label='Ford')
plt.legend()
plt.ylabel('Total Traded')

# Plot Moving Averages
gm['MA50'] = gm['Open'].rolling(50).mean()
gm['MA200'] = gm['Open'].rolling(200).mean()
gm[['Open','MA50','MA200']].plot(label='GM',figsize=(16,8))

# Plot Car Data Scatter_Matrix
car_comp = pd.concat([tesla['Open'],gm['Open'],ford['Open']],axis=1)
car_comp.columns = ['Tesla Open', 'GM Open', 'Ford Open']
scatter_matrix(car_comp,figsize=(8,8),alpha=0.2,hist_kwds={'bins':50})

# CandleStick Chart

# Reset Index to get a new col of Jan Dates
ford_reset = ford.loc['2012-01':'2012-01'].reset_index()

# Create new col of numerical "date" values
ford_reset['date_ax'] = ford_reset['Date'].apply(lambda date: date2num(date))
ford_values = [tuple(vals) for vals in ford_reset[['date_ax', 'Open', 'High', 'Low', 'Close']].values]

mondays = WeekdayLocator(MONDAY) # major ticks on the mondays
alldays = DayLocator() #minor ticks on the days
weekFormatter = DateFormatter('%b %d')
dayFormatter = DateFormatter('%d')

# Plot Data
fig, ax = plt.subplots()
fig.subplots.adjust(bottom=0.2)
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_minor_locator(alldays)
ax.xaxis.set_major_formatter(weekFormatter)

candlestick_ohlc(ax, ford_values, width=0.6, colorup='g', colordown='r')

# Basic Financial Analysis

# Calculate Daily Perecentage Change
# rt=ptpt−1−1rt=ptpt−1−1
# Defines r_t (return at time t) as equal to the price at time t divided by the price at time t-1 (previous day).
# Informs of percentage loss or gain if you bought the stock & sold on next day. Helps analyze volatility.

# Create new column "returns"
# tesla['returns'] = (tesla['Close']  / tesla['Close'].shift(1) ) - 1
tesla['returns'] = tesla['Close'].pct_change(1)
ford['returns'] = ford['Close'].pct_change(1)
gm['returns'] = gm['Close'].pct_change(1)

# Plot Histogram of returns
tesla['returns'].hist(bins=100,label='Tesla',figsize=(10,8),alpha=0.5)
gm['returns'].hist(bins=100,label='GM',alpha=0.5)
ford['returns'].hist(bins=100,label='Ford',alpha=0.5)
plt.legend()
plt.show()

# Plot KDE for another viewpoint
tesla['returns'].plot(kind='kde',label='Tesla',figsize=(12,6))
gm['returns'].plot(kind='kde',label='GM')
ford['returns'].plot(kind='kde',label='Ford')
plt.legend()
plt.show()

# Box Plots
box_df = pd.concat([tesla['returns'],gm['returns'],ford['returns']],axis=1)
box_df.columns = ['Tesla Returns', 'GM Returns', 'Ford Returns']
box_df.plot(kind='box',figsize=(8,11),colormap='jet')

# Compare Daily Return Between Stocks
scatter_matrix(box_df,figsize=(8,8),alpha=0.2,hist_kwds={'bins':50})

box_df.plot(kind='scatter',x='GM Returns',y='Ford Returns',alpha=0.4,figsize=(10,8))

# Create cumulative daily return column
tesla['Cumulative Return'] = (1 + tesla['returns']).cumprod()
print(tesla.head())
ford['Cumulative Return'] = (1 + ford['returns']).cumprod()
gm['Cumulative Return'] = (1 + gm['returns']).cumprod()

# plot cumulative return
tesla['Cumulative Return'].plot(label='Tesla',figsize=(16,8),title='Cumulative Return')
ford['Cumulative Return'].plot(label='Ford')
gm['Cumulative Return'].plot(label='GM')


import pandas as pd
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.linear_model import LinearRegression

dataset = pd.read_csv('50_Startups.csv')
X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, 4].values

# encoding categorical data
labelencoder = LabelEncoder()
X[:, 3] = labelencoder.fit_transform(X[:, 3])
onehotencoder = OneHotEncoder(categorical_features = [3])
X = onehotencoder.fit_transform(X).toarray()

# Avoiding the dummy variable trap
X = X[:, 1:]

# Splitting the dataset into the Training set and Test set
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

# Fitting Multiple Linear Regression to the Training set
regressor = LinearRegression()
regressor.fit(X_train, y_train)

# Predicting the Test set results
y_pred = regressor.predict(X_test)
print('test data \n', y_test)
print('predicted data \n', y_pred)


"""
# Definition for an Interval.
class Interval:
    def __init__(self, start: int = None, end: int = None):
        self.start = start
        self.end = end
"""

class Solution:
    def employeeFreeTime(self, schedule: '[[Interval]]') -> '[Interval]':
        intervals = sorted([i for s in schedule for i in s], key=lambda x: x.start)
        ret, pre = [], intervals[0]

        for interval in intervals[1:]:
            if interval.start <= pre.end and interval.end > pre.end:
                pre.end = interval.end
            elif interval.start > pre.end:
                ret.append(Interval(pre.end, interval.start))
                pre = interval
        return ret