#   Copyright 2019 PyFLOSIC developers
#
#   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.
#
import time
import copy
import numpy as np
from ase import neighborlist as NL
from flosic_os import xyz_to_nuclei_fod,ase2pyscf
from ase import io, units, Atoms, Atom
from ase.utils import natural_cutoffs
from pyscf import gto, dft
from flosic_scf import FLOSIC
from ase.constraints import FixAtoms
import sys

#        >>> mol = gto.M(atom='H 0 0 0; H 0 0 1.1')
#        >>> grids = dft.gen_grid.Grids(mol)
#        >>> grids.level = 4
#        >>> grids.build()

# We want to have
# - a list of all neigbors of each atom
# - a list of FOD's that correspond to each atom
# - a list of all neigbors for each FOD'

# - different dft mesh's for each subgroup of a molecule

class ON(object):
    """Provides functionality to implement O(N) scaling FLOSIC"""
    def __init__(self, mol, fod, grid_level=9):
        super(ON, self).__init__()
        self.mol = mol
        self.nspin = len(fod)
        self._fod = fod   # list of positions, Angst
        self.grid_level = grid_level
        self.eps_cutoff = 1.0e-10
        self.is_init = True
        self.add_ghosts = False
        self.nshell = 2
        #print(self.grid_level)
        #sys.exit()

    def build(self):
        """build the required data structures (e.g. neigbor lists)"""
        # convert to Bohr as internal unit
        self.onatoms = list()
        for s in range(self.nspin):
            self.onatoms.append(dict())

        self.fod = list()
        self.nfod = [0,0]
        for s in range(self.nspin):
            self.fod.append( self._fod[s].copy())
            self.nfod[s] = self._fod[s].shape[0]
            #print np.array_str(self.fod[s],precision=4)

        # build atom object (make sure to enter ccors in Angst)
        acoord = self.mol.atom_coords()
        self.atoms = Atoms()
        for na in range(self.mol.natm):
            aa = Atom(symbol=self.mol.atom_symbol(na),
                position=acoord[na]*units.Bohr)
            self.atoms.extend(aa)

        cutoffs = natural_cutoffs(self.atoms)

        ##print cutoffs
        self.nl = NL.NeighborList(cutoffs,
            self_interaction=False, bothways=True)
        self.nl.update(self.atoms)

        ##print self.nl.get_neighbors(1)

        # cut out the H atoms from the distances
        #non_H_ids = list()
        #for atmid in range(self.mol.natm):
        #    if self.mol.atom_pure_symbol(atmid) == 'H': continue
        #    non_H_ids.append(atmid)
        #
        #non_H_pos = np.zeros((len(non_H_ids),3), dtype=np.float64)
        #for atmid,i in zip(non_H_ids,range(len(non_H_ids))):
        #    non_H_pos[i,:] = self.atoms.positions[atmid,:]

        #self.fod_atm = [[[-1,0.0,'C']*4],[[-2,0.0,'C']*4]]
        #print self.fod_atm[0]
        #self.fod[0].shape[0],self.fod[1].shape[0]
        self.fod_atm = list()
        # find out the nearest atom for each FOD and deciced
        # if its a core FOD or not
        for s in range(self.nspin):
            lfod_atm = list()
            for i in range(self.fod[s].shape[0]):
                fpos = self.fod[s][i]
                # distance to atoms
                dists = np.linalg.norm(fpos - self.atoms.positions,
                    axis=1)
                # do not use H-Atoms as nearest atoms
                ite = 0
                nearest_atom = np.argsort(dists)[ite]
                while self.mol.atom_pure_symbol(nearest_atom) == 'H':
                    ite += 1
                    nearest_atom = np.argsort(dists)[ite]

                #print self.fod_atm
                #print self.fod_atm[s]
                #self.fod_atm[s][i][0] = nearest_atom
                #self.fod_atm[s][i][1] = dists[nearest_atom]
                #print dists[nearest_atom]
                fodtype = 'V'
                if dists[nearest_atom] < 0.175:
                    fodtype = 'C'
                if dists[nearest_atom] < 1.0e-4:
                    fodtype = 'C1s'
                #self.fod_atm[s][i][2] = fodtype
                #print fodtype
                #sys.exit()
                lfod_atm.append(
                    (nearest_atom,dists[nearest_atom],fodtype))
            self.fod_atm.append(lfod_atm)
        #print len(self.fod_atm)

        # make a list of all fods that correspond to a specific atom
        self.atm_fod = list()
        for na in range(self.mol.natm):
            lfodlist = [[],[]]
            for s in range(self.nspin):
                for i in range(self.nfod[s]):
                    if self.fod_atm[s][i][0] == na:
                        #print "fod", i, "belongs to atom", na
                        lfodlist[s].append(i)
            self.atm_fod.append(lfodlist)
        #print self.atm_fod

        # prepare the ao slices
        self.fod_ao_slc = list()
        for s in range(self.nspin):
            lfod_slc = list()
            for i in range(self.nfod[s]):
                slc = self.get_nbas_slices(s,i)
                lfod_slc.append(slc)
            self.fod_ao_slc.append(lfod_slc)
        #print len(self.fod_ao_slc[0]), len(self.fod_ao_slc)

        # prepare the ongrids
        print("Generating O(N) meshes: nshell={}, grid level={}"\
            .format(self.nshell, self.grid_level))
        self.fod_onmsh = list()
        self.fod_onmol = list()
        for s in range(self.nspin):
            lfod_onmsh = list()
            lfod_onmol = list()
            for i in range(self.nfod[s]):
                omol, ogrid = self.get_grid(s,i,lfod_onmsh)
                lfod_onmsh.append(ogrid)
                lfod_onmol.append(omol)
            self.fod_onmsh.append(lfod_onmsh)
            self.fod_onmol.append(lfod_onmol)

        print("Generating O(N) FOD lists ...")
        #print self.fod_onmsh
        #print self.onatoms
        #sys.exit()

        # prepare a list that contains all fods that
        # correspond to a given fod
        self.fod_fod = [[],[]]
        for s in range(self.nspin):
            for i in range(self.nfod[s]):
                # get the atom numbers that corresponds to the fod
                #print self.fod_atm[s][i]
                #print self.atm_fod[s][i]
                atids = self.onatoms[s][i]
                lfod_fod = list()
                for atid in atids:
                    # now find out which of all the other fods
                    # correspond to these atoms
                    for j in range(self.nfod[s]):
                        if i == j: continue
                        atnr = self.fod_atm[s][j][0]
                        if atnr in atids:
                            lfod_fod.append(j)
                # remove duplicates
                lfod_fod = list(set(lfod_fod))
                #print(lfod_fod)
                #print i, len(lfod_fod)
                self.fod_fod[s].append(lfod_fod)
        #print self.fod_fod[0]
        #print self.fod_fod[1]
        #sys.exit()

        # finally, build a fod-grp list
        # this list orders all fod that have the same mesh
        self.fodlist2group()

    def update(self,s,fpos):
        '''update the class according to the given (new) fod positions [in Angst.]'''
        pass

    def fodlist2group(self):
        """docstring for fodlist2group"""
        print('generate fodgroup list')
        # pre-generate the data structure
        self.fodgrps = list()
        for s in range(self.nspin):
            self.fodgrps.append(list())

        #print(self.fodgrps)
        #print(len(self.fodgrps))

        #for j in range(self.nfod[0]):
        #    print(self.onatoms[0][j])
        #sys.exit()

        # self.onatoms hält zu jedem fod id die entsprechenden
        # atome, die das mesh definieren
        for s in range(self.nspin):
            ttable =  [False for i in range(self.nfod[s])]
            #print(ttable)
            grpcnt = 0
            for j in range(self.nfod[s]):
                if ttable[j]: continue
                self.fodgrps[s].append([j])
                jonatoms = self.onatoms[s][j]
                jonatoms.sort()
                for jj in range(j+1,self.nfod[s]):
                    if ttable[jj]: continue
                    cpmonatoms = self.onatoms[s][jj]
                    cpmonatoms.sort()
                    if jonatoms == cpmonatoms:
                        #print('fod {} and {} have same onatoms {}'.format(j, jj, jonatoms))
                        self.fodgrps[s][grpcnt].append(jj)
                        ttable[jj] = True
                grpcnt += 1
                #print(self.fodgrps[s])
                #sys.exit()
        return

    def get_grid(self,s,fodid,lmsh,level=None,verbose=False):
        """Generate a grid object that corresponds to the
            O(N) structure for the given FOD
        """
        print(' -> building Vxc-Grid for FOD {} ...'.format(fodid), flush=True)
        #mol.atom_pure_symbol
        if level == None:
            level = self.grid_level
        onatoms = self.onatoms[s][fodid]
        #print(onatoms)

        #print('onatoms:', onatoms)
        #sys.exit()
        fodtype = self.fod_atm[s][fodid][2]
        ongrid = None

        # check if we already have meshes available
        # with the same onatoms
        #self.mf.on.fod_onmsh[self.s][fgrp[0]]
        onatoms = list(sorted(onatoms))
        pmshid = -1
        for j in range(fodid):
            #qq = list(sorted(self.onatoms[s][j]))
            #print(j, onatoms, qq)
            #print(lmsh)
            #self.fod_onmsh
            #print(onatoms)
            #print(self.onatoms[s][j].sort())
            if onatoms == list(sorted(self.onatoms[s][j])):
                ## we already have a mesh generated
                ongrid = lmsh[j]
                pmshid = j
        #sys.exit()
        # build string to generate Mole object
        mstr = ''
        for na in onatoms:
            sym = self.mol.atom_pure_symbol(na)
            pos = self.atoms.positions[na]
            #print sym, pos
            mstr += "{0} {1:0.12f} {2:0.12f} {3:0.12f};".format(
                sym,pos[0],pos[1],pos[2])

        # add ghost atom for valence descriptors
        if (fodtype == 'V') and (self.add_ghosts):
            sym = 'ghost:H'
            pos = self.fod[s][fodid]
            #print sym, pos
            mstr += "{0} {1:0.12f} {2:0.12f} {3:0.12f};".format(
                sym,pos[0],pos[1],pos[2])

        print('     type {}, na {}:  atoms in msh {}'.format(fodtype, self.fod_atm[s][fodid][0], onatoms))

        # build a Mole object from subsystem
        #isinstance(o, str):
        #if type(self.mol.basis) == dict:
        #    b = self.mol.basis
        #    b['ghost'] = gto.basis.load('sto3g', 'H')
        #else:
        #    b = {'default':self.mol.basis, 'ghost': gto.basis.load('sto3g', 'H')}

        b = self.mol.basis

        #print(mstr)
        #print("{0} {1}".format(fodid,b))

        #sys.exit()

        try:
            onmol =  gto.M(atom=mstr,basis=b)
        except RuntimeError:
            onmol =  gto.M(atom=mstr, basis=b, spin=1)
        onmol.verbose=0
        onmol.max_memory=1000
        #print(onmol.atom)
        # build the meshes
        if ongrid is None:
            _mdft = dft.UKS(onmol)
            _mdft.max_cycle = 0
            _mdft.grids.level = level
            _mdft.kernel()
            ongrid = copy.copy(_mdft.grids)
        else:
            print('     (mesh was already generated for fod {})'.format(pmshid))
        #ongrid = dft.gen_grid.Grids(onmol)
        #ongrid.prune = dft.gen_grid.nwchem_prune
        #ongrid.level = level
        #ongrid.build()

        #print(level)
        #print(ongrid.coords.shape)
        #print onmol
        #sys.exit()

        return (onmol,ongrid)

    # now we want to find out the basis functions that are
    # really needed for each FO
    # - we check the nearest atom
    # - we find the neigbors to that atom
    # - we find the nbas indices for those atoms (start, stop)
    #   and store them in a list
    def get_nbas_slices(self,s,fodid,verbose=False):
        """docstring for get_nbas_slices"""
        slices = list()
        near_atm = self.fod_atm[s][fodid]
        onatoms = [near_atm[0]]
        if (near_atm[2] == 'C') or (near_atm[2] == 'C1s'):
            if self.nshell == 2:
                nei_atm = self.get_neighbors(near_atm[0], nshell=1)
                for na in nei_atm:
                    onatoms.append(na)

        if near_atm[2] == 'V':
            nei_atm = self.get_neighbors(near_atm[0], nshell=self.nshell)
            for na in nei_atm:
                onatoms.append(na)

        # remove duplicates
        onatoms = list(set(onatoms))

        if self.nshell == -1:
            onatoms = list(range(self.mol.natm))

        # update class onatoms dict
        self.onatoms[s][fodid] = onatoms

        all_slices=self.mol.aoslice_by_atom()
        #print all_slices
        onnbas = 0
        for na in onatoms:
            lslice=(all_slices[na,2],all_slices[na,3])
            slices.append(lslice)
            onnbas += lslice[1]-lslice[0]

        # print all_slices[-1][-1]
        pc = 100.0 - onnbas / (all_slices[-1,-1] / 100.0)

        if verbose:
            print("Sparsity: {0} / {1} ({2:0.2f} %)"
                .format(onnbas,all_slices[-1,-1], pc))

        return slices

        #sys.exit()

    def get_on_dm(self,s,fodid,dm,flo=None):
        """Mask out all basis functions that are not required
        return FLO's and dm were unused ebtries are set to zero

        if condense is set to True, return dm and FLO's with only
        the size needed for the O(N) method (e.g. reduced)

        """
        if dm.shape[-1] != self.mol.aoslice_by_atom()[-1,-1]:
            print("DM has wrong shape")
            print(self.mol.aoslice_by_atom()[-1,-1])
            sys.exit()

        # find out which indices in the density matrix
        # belong to a given fod
        slcs = self.fod_ao_slc[s][fodid]
        slc = list()
        for sl in slcs:
            slc += list(range(sl[0],sl[1]))

        dmout = dm.copy()
        #floout = flo.copy()

        # zero out the dm
        for i in range(dm.shape[-1]):
            if i in slc: continue
            for j in range(dm.shape[-1]):
                if j in slc: continue
                #print "set dm zero", i, j
                dmout[i,j] = 0.0

        nchk = np.sum(dmout)
        if np.isnan(nchk):
            print('Got it')
            sys.exit()

        return dmout

    def get_neighbors(self, atmid, nshell=1):
        """
        Return the indices of the neighbor atoms to a given atom id.

        Args:
            atmid : int
                id of the atom for which the neigbors shall be returned

        Kwargs:
            nshell : int
                number of shells around the given atom that should be
                considered as neigbors
                1 -> means include only 'nearest neigbors'
                2 -> include nearest and next-nearest neigbors
                3 -> (and larger not yet implemented!)

        Returns:
            List of integers which are the indices of the neigbor atoms
            to atmid.
        """
        #if (nshell < 1) or (nshell > 2):
        #    raise ValueError("nshell must be 1 or 2")
        if nshell == -1:
            ret = list(range(self.mol.natm))
            return ret

        if nshell == 1:
            ret = list(self.nl.get_neighbors(atmid)[0])
        else:
            base = list(self.nl.get_neighbors(atmid)[0])
            nn = []
            for aid in base:
                nn += list(self.nl.get_neighbors(aid)[0])
            base.extend(nn)

            # remove duplicates
            ret = list(dict.fromkeys(base))

        return ret

    def print_stats(self):
        '''Print out statistics'''
        print('    --- O(N) stats ---')
        print('{:>4} {:>4} : {:>7} {:>7} {:>12}'.format('spin','nfod', 'nmsh', 'nbas', 'onatoms'))
        _nmshs = 0
        _nbas = 0
        for s in range(self.nspin):
            for j in range(self.nfod[s]):
                onmsh = self.fod_onmsh[s][j]
                nonmsh = onmsh.coords.shape[0]
                onat = self.onatoms[s][j]
                nbas = self.fod_onmol[s][j].aoslice_by_atom()[-1,-1]
                _nbas += nbas
                _nmshs += nonmsh
                print('{:4d} {:4d} : {:7d} {:7d}    {}'.format(s,j,nonmsh,nbas, onat))
        print('    ----------------')
        _nbas = float(_nbas)/(np.sum(self.nfod))
        _tnbas = self.mol.aoslice_by_atom()[-1,-1]
        _nmshs = float(_nmshs)/(np.sum(self.nfod))
        #print(_tnbas)
        print('vsic parameters\n')
        print('nmsh: avg {:7d}'.format(int(_nmshs)))
        print('nbas: avg {:7d}'.format(int(_nbas)))
        print('spar: {:11.1f} %'.format(100.0 - _nbas/(_tnbas*0.01)))
        print('')
        print('Number of fod-groups with identical mesh: {}'.format(len(self.fodgrps[0])))
        print('    ----------------')

def C1s_FixPos(on, s):
    '''Returns an ASE constraint that fixes the 1s core descriptor positions

    Args:
        on : ON object
            the object that corresponds to the mol/fod structure

        s : spin index

    Returns:
        ase.constraint object if there are C1s to fix, None otherwise
    '''
    c1sidx = list()
    mol = on.mol
    for fodid in range(on.nfod[s]):
        _na = mol.atom_pure_symbol(on.fod_atm[s][fodid][0])
        _ft = on.fod_atm[s][fodid][2]
        if (_ft == 'C1s'):
            c1sidx.append(fodid)

    c1score = None
    if len(c1sidx) > 0:
        c1score = FixAtoms(indices=c1sidx)

    return c1score

if __name__ == '__main__':
    CH3SH = '''
    C -0.04795000 +1.14952000 +0.00000000
    S -0.04795000 -0.66486000 +0.00000000
    H +1.28308000 -0.82325000 +0.00000000
    H -1.09260000 +1.46143000 +0.00000000
    H +0.43225000 +1.55121000 +0.89226000
    H +0.43225000 +1.55121000 -0.89226000
    '''
    # this are the spin-up descriptors
    fod1 = Atoms([
    Atom('X', (-0.04795000, -0.66486000, +0.00000000)),
    Atom('X', (-0.04795000, +1.14952000, +0.00000000)),
    Atom('X', (-1.01954312, +1.27662578, +0.00065565)),
    Atom('X', (+1.01316012, -0.72796570, -0.00302478)),
    Atom('X', (+0.41874165, +1.34380502, +0.80870475)),
    Atom('X', (+0.42024357, +1.34411742, -0.81146545)),
    Atom('X', (-0.46764078, -0.98842277, -0.72314717)),
    Atom('X', (-0.46848962, -0.97040067, +0.72108036)),
    Atom('X', (+0.01320210, +0.30892333, +0.00444147)),
    Atom('X', (-0.28022018, -0.62888360, -0.03731204)),
    Atom('X', (+0.05389371, -0.57381853, +0.19630494)),
    Atom('X', (+0.09262866, -0.55485889, -0.15751914)),
    Atom('X', (-0.05807583, -0.90413106, -0.00104673))])

    # this are the spin-down descriptors
    fod2 = Atoms([
    Atom('He',( -0.04795000, -0.66486000, +0.0000000)),
    Atom('He',( -0.04795000, +1.14952000, +0.0000000)),
    Atom('He',( +1.12523084, -0.68699049, +0.0301970)),
    Atom('He',( +0.40996981, +1.33508869, +0.8089839)),
    Atom('He',( +0.40987059, +1.34148952, -0.8106910)),
    Atom('He',( -0.49563876, -0.99517303, +0.6829207)),
    Atom('He',( -0.49640020, -0.89986161, -0.6743094)),
    Atom('He',( +0.00073876, +0.28757089, -0.0298617)),
    Atom('He',( -1.03186573, +1.29783767, -0.0035536)),
    Atom('He',( +0.04235081, -0.54885843, +0.1924678)),
    Atom('He',( +0.07365725, -0.59150454, -0.1951675)),
    Atom('He',( -0.28422422, -0.61466396, -0.0087913)),
    Atom('He',( -0.02352948, -1.0425011 ,+0.01253239))])

    b = 'ccpvdz'
    spin = 0
    charge = 0

    mol = gto.M(atom=CH3SH,
                basis=b,
                spin=spin,
                charge=charge)

    grid_level  = 7
    mol.verbose = 4
    mol.max_memory = 2000
    mol.build()
    xc = 'LDA,PW'

    # quick dft calculation
    mdft = dft.UKS(mol)
    mdft.xc = xc
    mdft.kernel()

    # build O(N) stuff
    myon = ON(mol,[fod1.positions,fod2.positions], grid_level=grid_level)
    myon.nshell = 2
    myon.build()

    # enable ONMSH
    m = FLOSIC(mol,xc=xc,fod1=fod1,fod2=fod2,grid_level=grid_level, init_dm=mdft.make_rdm1())
    m.max_cycle = 40
    m.set_on(myon)
    m.conv_tol = 1e-5

    m.preopt = False
    m.preopt_start_cycle=0
    m.preopt_fix1s = True
    m.preopt_fmin = 0.005

    m.kernel()

    print(m.fod_gradients())
    print(m.get_fforces())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
from timeit import default_timer as timer

import argparse
import subprocess
import sys
import scipy.io.wavfile as wav
import numpy as np
from deepspeech.model import Model

# These constants control the beam search decoder

# Beam width used in the CTC decoder when building candidate transcriptions
BEAM_WIDTH = 500

# The alpha hyperparameter of the CTC decoder. Language Model weight
LM_WEIGHT = 1.75

# The beta hyperparameter of the CTC decoder. Word insertion weight (penalty)
WORD_COUNT_WEIGHT = 1.00

# Valid word insertion weight. This is used to lessen the word insertion penalty
# when the inserted word is part of the vocabulary
VALID_WORD_COUNT_WEIGHT = 1.00

# These constants are tied to the shape of the graph used (changing them changes
# the geometry of the first layer), so make sure you use the same constants that
# were used during training

# Number of MFCC features to use
N_FEATURES = 26

# Size of the context window used for producing timesteps in the input vector
N_CONTEXT = 9

def convert_samplerate(audio_path):
    sox_cmd = 'sox {} --type raw --bits 16 --channels 1 --rate 16000 - '.format(audio_path)
    try:
        p = subprocess.Popen(sox_cmd.split(),
                             stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        output, err = p.communicate()

        if p.returncode:
            raise RuntimeError('SoX returned non-zero status: {}'.format(err))

    except OSError as e:
        raise OSError('SoX not found, use 16kHz files or install it: ', e)

    audio = np.fromstring(output, dtype=np.int16)
    return 16000, audio

def main():
    parser = argparse.ArgumentParser(description='Benchmarking tooling for DeepSpeech native_client.')
    parser.add_argument('model',
                        help='Path to the model (protocol buffer binary file)')
    parser.add_argument('alphabet',
                        help='Path to the configuration file specifying the alphabet used by the network')
    parser.add_argument('lm', nargs='?',
                        help='Path to the language model binary file')
    parser.add_argument('trie', nargs='?',
                        help='Path to the language model trie file created with native_client/generate_trie')
    parser.add_argument('audio',
                        help='Path to the audio file to run (WAV format)')
    args = parser.parse_args()

    print('Loading model from file %s' % (args.model), file=sys.stderr)
    model_load_start = timer()
    ds = Model(args.model, N_FEATURES, N_CONTEXT, args.alphabet, BEAM_WIDTH)
    model_load_end = timer() - model_load_start
    print('Loaded model in %0.3fs.' % (model_load_end), file=sys.stderr)

    if args.lm and args.trie:
        print('Loading language model from files %s %s' % (args.lm, args.trie), file=sys.stderr)
        lm_load_start = timer()
        ds.enableDecoderWithLM(args.alphabet, args.lm, args.trie, LM_WEIGHT,
                               WORD_COUNT_WEIGHT, VALID_WORD_COUNT_WEIGHT)
        lm_load_end = timer() - lm_load_start
        print('Loaded language model in %0.3fs.' % (lm_load_end), file=sys.stderr)

    fs, audio = wav.read(args.audio)
    if fs != 16000:
        if fs < 16000:
            print('Warning: original sample rate (%d) is lower than 16kHz. Up-sampling might produce erratic speech recognition.' % (fs), file=sys.stderr)
        fs, audio = convert_samplerate(args.audio)
    audio_length = len(audio) * ( 1 / 16000)

    print('Running inference.', file=sys.stderr)
    inference_start = timer()
    print(ds.stt(audio, fs))
    inference_end = timer() - inference_start
    print('Inference took %0.3fs for %0.3fs audio file.' % (inference_end, audio_length), file=sys.stderr)

if __name__ == '__main__':
    main()


# This code is built from the PyTorch examples repository: https://github.com/pytorch/vision/tree/master/torchvision/models.
# Copyright (c) 2017 Torch Contributors.
# The Pytorch examples are available under the BSD 3-Clause License.
#
# ==========================================================================================
#
# Adobe’s modifications are Copyright 2019 Adobe. All rights reserved.
# Adobe’s modifications are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
# 4.0 International Public License (CC-NC-SA-4.0). To view a copy of the license, visit
# https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.
#
# ==========================================================================================
#
# BSD-3 License
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import numpy as np
from .utils import *
from IPython import embed

__all__ = ['AlexNet', 'alexnet']

model_urls = {
    'alexnet': 'https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth',
}

class AlexNet(nn.Module):

    def __init__(self, num_classes=1000, filter_size=1, pool_only=False, relu_first=True):
        super(AlexNet, self).__init__()

        if(pool_only): # only apply LPF to pooling layers, so run conv1 at stride 4 as before
            first_ds = [nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),]
        else:
            if(relu_first): # this is the right order
                first_ds = [nn.Conv2d(3, 64, kernel_size=11, stride=2, padding=2),
                    nn.ReLU(inplace=True),
                    Downsample(filt_size=filter_size, stride=2, channels=64),]
            else: # this is the wrong order, since it's equivalent to downsampling the image first
                first_ds = [nn.Conv2d(3, 64, kernel_size=11, stride=2, padding=2),
                    Downsample(filt_size=filter_size, stride=2, channels=64),
                    nn.ReLU(inplace=True),]

        first_ds += [nn.MaxPool2d(kernel_size=3, stride=1),
            Downsample(filt_size=filter_size, stride=2, channels=64),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=1),
            Downsample(filt_size=filter_size, stride=2, channels=192),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=1),
            Downsample(filt_size=filter_size, stride=2, channels=256)]
        self.features = nn.Sequential(*first_ds)

        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = x.view(x.size(0), 256 * 6 * 6)
        x = self.classifier(x)
        return x

def alexnet(pretrained=False, **kwargs):
    """AlexNet model architecture from the
    `"One weird trick..." <https://arxiv.org/abs/1404.5997>`_ paper.

    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    model = AlexNet(**kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['alexnet']))
    return model

# replacing MaxPool with BlurPool layers
class AlexNetNMP(nn.Module):

    def __init__(self, num_classes=1000, filter_size=1):
        super(AlexNetNMP, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            Downsample(filt_size=filter_size, stride=2, channels=64, pad_off=-1, hidden=True),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            Downsample(filt_size=filter_size, stride=2, channels=192, pad_off=-1, hidden=True),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            Downsample(filt_size=filter_size, stride=2, channels=256, pad_off=-1, hidden=True),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        # embed()
        x = self.features(x)
        x = self.avgpool(x)
        x = x.view(x.size(0), 256 * 6 * 6)
        x = self.classifier(x)
        return x

def alexnetnmp(pretrained=False, **kwargs):
    """AlexNet model architecture from the
    `"One weird trick..." <https://arxiv.org/abs/1404.5997>`_ paper.

    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    model = AlexNetNMP(**kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['alexnet']))
    return model

    # def __init__(self, num_classes=1000):
        # super(AlexNet, self).__init__()
        # self.features = nn.Sequential(
        #     nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
        #     nn.ReLU(inplace=True),
        #     nn.MaxPool2d(kernel_size=3, stride=2),
        #     nn.Conv2d(64, 192, kernel_size=5, padding=2),
        #     nn.ReLU(inplace=True),
        #     nn.MaxPool2d(kernel_size=3, stride=2),
        #     nn.Conv2d(192, 384, kernel_size=3, padding=1),
        #     nn.ReLU(inplace=True),
        #     nn.Conv2d(384, 256, kernel_size=3, padding=1),
        #     nn.ReLU(inplace=True),
        #     nn.Conv2d(256, 256, kernel_size=3, padding=1),
        #     nn.ReLU(inplace=True),
        #     nn.MaxPool2d(kernel_size=3, stride=2),
        # )
        # self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        # self.classifier = nn.Sequential(
        #     nn.Dropout(),
        #     nn.Linear(256 * 6 * 6, 4096),
        #     nn.ReLU(inplace=True),
        #     nn.Dropout(),
        #     nn.Linear(4096, 4096),
        #     nn.ReLU(inplace=True),
        #     nn.Linear(4096, num_classes),
        # )


# -*- coding: utf-8 -*-
"""
Created on Tue Jan 19 11:41:56 2021

@author: Administrator
"""
from keras.models import load_model
import cv2
import numpy as np
import tkinter
from tkinter import messagebox
import smtplib

root = tkinter.Tk()
root.withdraw()

prototxtPath = r"face_detector\deploy.prototxt"
weightsPath = r"face_detector\res10_300x300_ssd_iter_140000.caffemodel"
faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)

model = load_model("mask_detector.model")

# initialize the video stream
print("[INFO] starting video stream...")
vid_source = cv2.VideoCapture(0)

text_dict = {0:'Mask ON',1:'No Mask'}
rect_color_dict = {0:(0,255,0), 1:(0,0,255)}

SUBJECT = "Subject"
TEXT = "One Visitor violated the Face Mask Policy. See in the camera to recognise the user.";

while(True):
    ret,img = vid_source.read()
    grayscale_img = cv2.cvtColor(img,cv2.COLOR_BGR)
    faces = faceNet.detectMutltiScale(grayscale_img,1.3,5)

    for(x,y,w,h) in faces:
        face_img = grayscale_img[y:u+w,x:x+w]
        resized_img = cv2.resize(face_img,(224,224))
        normalized_img = resized_img/255.0
        reshaped_img = np.reshape(normalized_img,(1,224,224,1))
        result=model.predict(reshaped_img)

        label = np.argmax(result,axis = 1)[0]

        cv2.rectangle(img, (x,y),(x+w,y+h),rect_color_dict[label],2)
        cv2.rectangle(img, (x,y-40),(x+w,y),rect_color_dict[label],-1)
        cv2.putText(img, text_dict[label], (x,y-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,0),2)

        if(label == 1):
            messagebox.showwarning("Warning", "Access Denied. Please wear a Face Mask")

            message = 'Subject : {}\n\n{}'.format(SUBJECT,TEXT)
            mail = smtplib.SMTP('smtp.gmail.com',587)
            mail.ehlo()
            mail.starttls()
            mail.login('vsoftnotification@gmail.com', 'mahalakshmi123')
            mail.sendmail('vsoftnotification@gmail.com', 'mahalakshmithirumurthy@gmail.com', message);
            mail.close()
        else:
            pass
            break
    cv2.imshow('LIVE Video Feed',img)
    key = cv2.waitKey(1)

    if(key == 27):
        break
cv2.destroyAllWindows()
source.release()


#from util.keys import AZURE_API_KEY
import asyncio, aiohttp, discord
from PIL import ImageFont
from PIL import ImageDraw
from PIL import Image
import json
import urllib.request
from discord.ext import commands
from discord.ext.commands.cooldowns import BucketType
from util.functions import Functions
import numpy as np
from random import randint

class Imagepaste():
    def __init__(self, client):
        self.client = client

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def epb(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        emojiface = Image.open('images/emojipasta.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            x, y = f['faceRectangle']['left'], f['faceRectangle']['top']

            current_emojiface = emojiface.resize((w, int(w * emojiface.size[1] / emojiface.size[0])), resample=Image.LANCZOS)
            current_emojiface = current_emojiface.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=False)

            draw_img.paste(current_emojiface, (x, y), current_emojiface)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def minion(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        glasses = Image.open('images/minionglasses.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            x, y = f['faceRectangle']['left'], f['faceRectangle']['top']

            current_emojiface = glasses.resize((w, int(w * glasses.size[1] / glasses.size[0])), resample=Image.LANCZOS)
            current_emojiface = current_emojiface.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=False)

            draw_img.paste(current_emojiface, (x, y), current_emojiface)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def lensflare(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        lf = Image.open('images/lensflare.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            left_eye = np.array([
                        [f['faceLandmarks']['eyeLeftOuter']['x'], f['faceLandmarks']['eyeLeftOuter']['y']],
                        [f['faceLandmarks']['eyeLeftBottom']['x'], f['faceLandmarks']['eyeLeftBottom']['y']],
                        [f['faceLandmarks']['eyeLeftTop']['x'], f['faceLandmarks']['eyeLeftTop']['y']],
                        [f['faceLandmarks']['eyeLeftInner']['x'], f['faceLandmarks']['eyeLeftInner']['y']]
                        ])
            right_eye = np.array([
                        [f['faceLandmarks']['eyeRightOuter']['x'], f['faceLandmarks']['eyeRightOuter']['y']],
                        [f['faceLandmarks']['eyeRightBottom']['x'], f['faceLandmarks']['eyeRightBottom']['y']],
                        [f['faceLandmarks']['eyeRightTop']['x'], f['faceLandmarks']['eyeRightTop']['y']],
                        [f['faceLandmarks']['eyeRightInner']['x'], f['faceLandmarks']['eyeRightInner']['y']]
                        ])

            left_center = left_eye.mean(axis=0).astype("int")
            right_center = right_eye.mean(axis=0).astype("int")

            current_lf = lf.resize((w, int(w * lf.size[1] / lf.size[0])), resample=Image.LANCZOS)
            current_lf = current_lf.rotate((f['faceAttributes']['headPose']['roll'] * -1) + 45, expand=True)

            w_offset, h_offset = int(current_lf.width / 2), int(current_lf.height / 2)
            draw_img.paste(current_lf, (left_center[0] - w_offset, left_center[1] - h_offset), current_lf)
            draw_img.paste(current_lf, (right_center[0] - w_offset, right_center[1] - h_offset), current_lf)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def hearteyes(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        lf = Image.open('images/heart.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            left_eye = np.array([
                        [f['faceLandmarks']['eyeLeftOuter']['x'], f['faceLandmarks']['eyeLeftOuter']['y']],
                        [f['faceLandmarks']['eyeLeftBottom']['x'], f['faceLandmarks']['eyeLeftBottom']['y']],
                        [f['faceLandmarks']['eyeLeftTop']['x'], f['faceLandmarks']['eyeLeftTop']['y']],
                        [f['faceLandmarks']['eyeLeftInner']['x'], f['faceLandmarks']['eyeLeftInner']['y']]
                        ])
            right_eye = np.array([
                        [f['faceLandmarks']['eyeRightOuter']['x'], f['faceLandmarks']['eyeRightOuter']['y']],
                        [f['faceLandmarks']['eyeRightBottom']['x'], f['faceLandmarks']['eyeRightBottom']['y']],
                        [f['faceLandmarks']['eyeRightTop']['x'], f['faceLandmarks']['eyeRightTop']['y']],
                        [f['faceLandmarks']['eyeRightInner']['x'], f['faceLandmarks']['eyeRightInner']['y']]
                        ])

            left_center = left_eye.mean(axis=0).astype("int")
            right_center = right_eye.mean(axis=0).astype("int")
            current_lf = lf.resize((int(w * .45), int(int(w * .45) * lf.size[1] / lf.size[0])), resample=Image.LANCZOS)

            w_offset, h_offset = int(current_lf.width / 2), int(current_lf.height / 2)
            leftheart = current_lf.rotate((f['faceAttributes']['headPose']['roll'] * -1) + 20, expand=False)
            rightheart = current_lf.rotate((f['faceAttributes']['headPose']['roll'] * -1) - 20, expand=False)

            draw_img.paste(leftheart, (left_center[0] - w_offset, left_center[1] - h_offset), leftheart)
            draw_img.paste(rightheart, (right_center[0] - w_offset, right_center[1] - h_offset), rightheart)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def mlp(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            r = randint(0,5)
            eyes = Image.open('images/mlp'+str(r)+'.png').convert("RGBA")

            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            x, y = f['faceRectangle']['left'], f['faceRectangle']['top']

            current_emojiface = eyes.resize((w, int(w * eyes.size[1] / eyes.size[0])), resample=Image.LANCZOS)
            current_emojiface = current_emojiface.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=False)

            draw_img.paste(current_emojiface, (x, y), current_emojiface)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def thuglife(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        glasses = Image.open('images/dealwithit.png').convert("RGBA")
        joint = Image.open('images/joint.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            nose = np.array([
                        [f['faceLandmarks']['noseRootLeft']['x'], f['faceLandmarks']['noseRootLeft']['y']],
                        [f['faceLandmarks']['noseRootRight']['x'], f['faceLandmarks']['noseRootRight']['y']]
                        ])

            nose_center = nose.mean(axis=0).astype("int")
            center_lip_y = abs((int(f['faceLandmarks']['upperLipTop']['y']) - int(f['faceLandmarks']['underLipBottom']['y'])) / 2)
            final_y = int(f['faceLandmarks']['upperLipTop']['y'] + center_lip_y)
            w2 = int(abs(f['faceLandmarks']['mouthLeft']['x'] - f['faceLandmarks']['mouthRight']['x']))
            final_x = int((w2 / 2) + f['faceLandmarks']['mouthLeft']['x'])

            current_glasses = glasses.resize((w, int(w * glasses.size[1] / glasses.size[0])), resample=Image.LANCZOS)
            current_glasses = current_glasses.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=True)
            offset_x, offset_y = int(current_glasses.width / 2), int(current_glasses.height / 2)

            new_joint = joint.resize((int(w2 * .75), int((w2 * .75) * joint.size[1] / joint.size[0])), resample=Image.LANCZOS)
            new_joint = new_joint.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=False)

            draw_img.paste(current_glasses, (nose_center[0] - offset_x, nose_center[1] - offset_y), current_glasses)
            draw_img.paste(new_joint, (final_x, final_y), new_joint)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def mustache(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        mustache = Image.open('images/mustache.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            nose = np.array([
                        [f['faceLandmarks']['noseRootLeft']['x'], f['faceLandmarks']['noseRootLeft']['y']],
                        [f['faceLandmarks']['noseRootRight']['x'], f['faceLandmarks']['noseRootRight']['y']]
                        ])

            nose_center = nose.mean(axis=0).astype("int")
            current_mustache = mustache.resize((w, int(w * mustache.size[1] / mustache.size[0])), resample=Image.LANCZOS)
            current_mustache = current_mustache.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=True)
            offset_x, offset_y = int(current_mustache.width / 2), int(current_mustache.height / 2)

            draw_img.paste(current_mustache, (nose_center[0] - offset_x, nose_center[1] - offset_y), current_mustache)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True)
    async def mood(self, ctx, url: str=None):
        url = await Imagepaste.get_attachment_images(self, ctx, url)
        faces = await Imagepaste.get_face_data(self, ctx, url)

        img = Image.open(str(ctx.message.channel.id)+'.png').convert("RGBA")
        draw_img = img.convert("RGBA")

        for f in faces:
            d = {}
            for emotion, value in f['faceAttributes']['emotion'].items():
                d[emotion] = value

            emotion = ""
            if f['faceAttributes']['glasses'] == "Sunglasses":
                emotion = "sunglasses"
            else:
                v = list(d.values())
                k = list(d.keys())
                emotion = k[v.index(max(v))]

            emojiface = Image.open('images/'+emotion+'.png').convert("RGBA")

            w, h = f['faceRectangle']['width'], f['faceRectangle']['height']
            x, y = f['faceRectangle']['left'], f['faceRectangle']['top']

            current_emojiface = emojiface.resize((w, int(w * emojiface.size[1] / emojiface.size[0])), resample=Image.LANCZOS)
            current_emojiface = current_emojiface.rotate(f['faceAttributes']['headPose']['roll'] * -1, expand=False)

            draw_img.paste(current_emojiface, (x, y), current_emojiface)

        draw_img.save('out.png')
        await self.client.send_file(ctx.message.channel, 'out.png')

    async def get_attachment_images(self, ctx, url):
        last_attachment = url
        if url:
            if Functions.is_image(url) is False:
                url = None
        if url is None:
            async for m in self.client.logs_from(ctx.message.channel, before=ctx.message, limit=20):
                if m.attachments:
                    if len(m.attachments) > 0 and 'url' in m.attachments[0]:
                        last_attachment = m.attachments[0]['url']
                        if not Functions.is_image(last_attachment):
                            continue
                        else:
                            break
                    else:
                        continue
                elif m.embeds:
                    if len(m.embeds) > 0 and 'url' in m.embeds[0]:
                        last_attachment = m.embeds[0]['url']
                        if not Functions.is_image(last_attachment):
                            continue
                        else:
                            break
                    else:
                        continue
        pic_name = str(ctx.message.channel.id)+'.png'
        headers = {'User-Agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'}
        f = urllib.request.Request(url=last_attachment,headers=headers)
        f = urllib.request.urlopen(f)
        with open(pic_name, "wb") as c:
            c.write(f.read())
        return last_attachment

    async def get_face_data(self, ctx, url):
        headers = {"Content-Type":"application/json", "Ocp-Apim-Subscription-Key": AZURE_API_KEY}
        data = {"url": url}
        faces = {}
        async with aiohttp.ClientSession() as session:
            async with session.post('https://eastus.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=false&returnFaceLandmarks=true&returnFaceAttributes=headPose,emotion,glasses', headers=headers, data=json.dumps(data)) as r:
                faces = await r.json()
        if len(faces) == 0:
            await self.client.say(":no_entry_sign: Unable to detect a face.")
            return
        if "error" in faces:
            await self.client.say(":warning: API error returned. Try again later.")
            return
        return faces

def setup(client):
    client.add_cog(Imagepaste(client))


from ausp import *
import numpy as np

datadir = os.path.expanduser('~/.au-sp')

def aulib(sound_dir):
    return os.path.join(datadir, 'audio', sound_dir)

shellbox_sound = RandomSound()
shellbox_sound.populate_with_dir(aulib('shellbox'))

dotquarter_dur = 60.0/30
eighth_dur = dotquarter_dur/3

def falling_sound(snd, dur, start_ratio=1):
    return ResampledSound(ClippedSound(snd, dur*start_ratio), lambda t: np.exp(1-t/dur)*start_ratio)

raw_sine_bass = RawPitchedSound(os.path.join(aulib('werb_sine'), 'werb_sine.0.110.wav'))

rasp_bass = RandomIntervalSound(RawPitchedSound(os.path.join(aulib('rasp_bass'),
'rasp_bass_1.0.110.wav')).for_pitch('D_2'), margin=.5)
string_bass = RandomIntervalSound(RawPitchedSound(os.path.join(aulib('bowed_violin'),
'bowed_violin_3.120.D_4.wav')).for_pitch('D_2'), margin=.5)

low_tom = RandomSound()
low_tom.populate_with_dir(aulib('long_low_tom'))

cym_snd = RandomSound()
cym_snd.populate_with_dir(aulib('hi_hat_close'))

bass_pulse = RandomSound()
bass_pulse.populate_with_dir(aulib('bass_pulse'))

one_shaker = RandomSound()
one_shaker.populate_with_dir(aulib('shaker_singles'))

def generate_all_tracks(master_beat):
    pickup, firstsec = master_beat.split(2)
    tr = Track('Pickup slide-downs', end_padding=40)
    pickup_tr_beat = tr.link_root(pickup)
    pickup_eighths = 9 # must be at least 4
    start_r = 1.0
    for i, b in enumerate(pickup_tr_beat.split_even(pickup_eighths*2)):
        start_r -= .025
        b.set_duration(eighth_dur/2)
        if i == pickup_eighths*2 - 4*2:
            pass
            #b.attach(shellbox_sound, Location((.1, .2), 2))
            #b.attach(shellbox_sound, Location((-.15, .2), 3))
        if i % 2 == 0:
            b.attach(ClippedSound(falling_sound(raw_sine_bass.for_pitch('A_0'),
                    dotquarter_dur, start_ratio=start_r), dotquarter_dur), Location((1, 1), .6))
            b.attach(bass_pulse, Location(1, 2, .4))
            steenth1, steenth2 = b.split([.55, .45])
            steenth2.attach(low_tom, Location(-5, 2, 2))
            b.attach(low_tom, Location(-5, 2, 2))
    yield tr
    tr2 = Track('Start raspy bass', end_padding=40)
    tr_beat = tr2.link_root(firstsec)
    sec_dur = dotquarter_dur*4*4
    tr_beat.set_duration(sec_dur)
    meas1and2, meas3, meas4 = tr_beat.split([2, 1, 1])
    meas1and2.attach(rasp_bass.for_interval(sec_dur/2), Location(-2, 2, .2))
    meas1and2.attach(string_bass.for_interval(sec_dur/2), Location(-5, 2, -2))
    meas1and2.attach(string_bass.for_interval(sec_dur/2), Location(5, -2, .2))
    meas4.attach(rasp_bass.for_interval(sec_dur/4), Location(-2, 2, .2))
    meas4.attach(string_bass.for_interval(sec_dur/4), Location(-5, 2, -2))
    meas4.attach(string_bass.for_interval(sec_dur/4), Location(5, -2, .2))
    yield tr2
    tr3 = Track('heartBeat')
    tr_beat = tr3.link_root(firstsec)
    for meas_b in tr_beat.split_even(4):
        for dotquart in meas_b.split_even(4):
            for eighth in dotquart.split_even(3):
                eighth.attach(SpreadSound(low_tom, (.1,)*3, .02, 3), Location(-2, 2, 2))
                eighth.attach(SpreadSound(one_shaker, (.1,)*3, .02, 3), Location(2, -2, 20))
                for beat32 in eighth.split([1, .8, .9, .8]):
                    spread_shake = SpreadSound(one_shaker, (.1,)*3, .01, 3)
                    beat32.attach(spread_shake, Location(4, -2, 30))
                    beat32.attach(ShiftedSound(spread_shake, -.05), Location(-4, 3, 40))
    yield tr3

mainbeat = Beat()
mix = Mixer('Box in the Shell!', tracks=list(generate_all_tracks(mainbeat)))
mix.play(quick_play=False) #, t0=15, t1=16)


'''
Generate plots for GENIE analysis data.
'''

from __future__ import division

import pandas as pd
import numpy as np
import plotly
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot

from g2p_client import G2PDatabase

# Constants
HOST = 'dms-dev.compbio.ohsu.edu'
GENIE_VARIANTS = '../../data/data_mutations_extended.txt'
GENIE_CLINICAL = '../../data/data_clinical.txt'
G2P_FILE = './data/g2p.tsv'
evidence_levels = ['A', 'B', 'C', 'D']
evidence_directions = ['resistant', 'responsive']

class GENIEAnalysis:

    def __init__(self, variants_file=GENIE_VARIANTS,
                 clinical_file=GENIE_CLINICAL, g2p_df=None, occurence_thres=2):
        self.variants_file = variants_file
        self.clinical_file = clinical_file
        self.genie_df = None
        self.g2p_df = g2p_df
        self.genie_g2p_df = None

        self.occurence_thres = occurence_thres
        self.num_variants = 0
        self.num_donors = 0
        self.num_shared_variants = 0
        self.num_donors_with_shared_variants = 0

    def load(self):
        '''
        Load GENIE variants and clinical data and return complete dataframe.
        '''

        # Load GENIE variants.
        genie_variants = pd.read_csv(self.variants_file, sep='\t', comment='#', low_memory=False)

        # Load GENIE clinical data.
        genie_clinical = pd.read_csv(self.clinical_file, sep='\t', comment='#')

        # Merge clinical data with variants.
        self.genie_df = pd.merge(genie_variants, genie_clinical, left_on=['Tumor_Sample_Barcode'], right_on=['SAMPLE_ID'])

        # Add count column to store total occurences of a variant.
        variant_counts = self.genie_df.groupby(['Chromosome', 'Start_Position', 'End_Position', 'Reference_Allele', 'Tumor_Seq_Allele2']).size().rename('count').reset_index()
        self.genie_df = pd.merge(self.genie_df, variant_counts, on=['Chromosome', 'Start_Position', 'End_Position', 'Reference_Allele', 'Tumor_Seq_Allele2'])
        # Set attributes.
        self.num_variants = len(self.genie_df)
        self.num_donors = get_num_donors(self.genie_df)
        shared_variants = self.get_shared_variants()
        self.num_shared_variants = len(shared_variants)
        self.num_donors_with_shared_variants = get_num_donors(shared_variants)

        # Merge
        # The BIG merge of GENIE with G2P. Left join preserves all genie keys and
        # creates rows where there is an entry in G2P matching GENIE.
        self.genie_g2p_df = pd.merge(self.genie_df, self.g2p_df, how='left',
                                     left_on=['Chromosome', 'Start_Position', 'Reference_Allele', 'Tumor_Seq_Allele2'],
                                     right_on=['feature.chromosome', 'feature.start', 'feature.ref', 'feature.alt'])

        return self.genie_g2p_df

    def get_shared_variants(self):
        return self.genie_df[self.genie_df['count'] >= self.occurence_thres]

    def donor_variant_histogram(self, filename='donor_variant_histogram'):
        '''
        Create histogram of variants per donor.
        '''
        x = self.genie_df['Tumor_Sample_Barcode'].value_counts().values
        data = [go.Histogram(x=x)]
        create_plot(data, title='GENIE: Variants per donor histogram',
                    xaxis_title='# variants per donor',
                    yaxis_title='count', filename=filename)

    def gene_variant_bar_chart(self, filename='gene_variant_counts'):
        '''
        Create plot of variants per gene.
        '''
        x = self.genie_df['Hugo_Symbol'].value_counts()
        data = [go.Bar(x=x.index, y=x.values)]
        create_plot(data, title='GENIE: Variants per donor histogram',
                    xaxis_title='# variants per donor',
                    yaxis_title='count', filename=filename)

    def matches_by_gene(self, filename='matches_by_gene'):
        ''' Matches by gene. '''
        genie_g2p_df_matches_df = self.genie_g2p_df[self.genie_g2p_df['evidence.id'].notnull()]
        counts = genie_g2p_df_matches_df['Hugo_Symbol'].value_counts()

        data = [
            go.Bar(
                x=counts.index,
                y=counts.values
            )
        ]

        create_plot(data, title='GENIE: Matches per gene', filename=filename)

# Helper methods.

def create_plot(data, layout=None, title='', xaxis_title= '', yaxis_title= '',
                width=600, height=600, filename='plot'):
    '''
    Create Plotly plot using offline approach.
    '''
    if not layout:
        layout = go.Layout(
            title=title,
            xaxis={
                'title': xaxis_title
            },
            yaxis={
                'title': yaxis_title
            },
            width=600,
            height=600
        )
    fig = go.Figure(data=data, layout=layout)
    plotly.offline.plot(fig, filename=filename)

def load_g2p_df(host, file, refresh=False):
    try:
        if refresh:
            raise Exception()
        g2p_df = pd.read_csv(file, sep='\t', encoding='utf-8')
    except:
        db = G2PDatabase(host)
        g2p_df = db.associations_dataframe()
        g2p_df.to_csv(file, sep='\t', encoding='utf-8')

    return g2p_df

def get_num_donors(df):
    ''' Returns number of donors in a dataframe. '''
    return len(df['Tumor_Sample_Barcode'].unique())

def print_cohort_stats(df):
    '''
    Prints number of (a) variants; (b) unique variants; and (c) donors
    in a dataframe.
    '''
    print '-------------'
    print 'Number of variants: %i' % len(df)
    #print 'Number of unique variants: %i' % len(df[df['count'] > 1])
    print 'Number of donors: %i' % get_num_donors(df)
    print '-------------'

if __name__ == '__main__':
    g2p_df = load_g2p_df(HOST, G2P_FILE)

    genie_analysis = GENIEAnalysis(g2p_df=g2p_df)
    genie_analysis.load()

    print "All of GENIE"
    print_cohort_stats(genie_analysis.genie_df)

    print ''
    print "GENIE filtered for variant count >= %i" % genie_analysis.occurence_thres
    print_cohort_stats(genie_analysis.get_shared_variants())

    genie_analysis.donor_variant_histogram()
    genie_analysis.gene_variant_bar_chart()
    genie_analysis.matches_by_gene()


#! /usr/bin/env python

"""
Module with ADI algorithm (median psf subtraction).
Carlos A. Gomez / ULg
"""

from __future__ import division

__author__ = 'C. Gomez @ ULg'
__all__ = ['adi']

import numpy as np
from ..conf import time_ini, timing
from ..var import get_annulus, mask_circle
from ..preproc import cube_derotate, cube_collapse, check_PA_vector
from ..pca.pca_local import define_annuli

def adi(cube, angle_list, fwhm=4, radius_int=0, asize=2, delta_rot=1,
        mode='fullfr', nframes=4, collapse='median', full_output=False,
        verbose=True):
    """ Algorithm based on Marois et al. 2006 on Angular Differential Imaging.
    First the median frame is subtracted, then the median of the four closest
    frames taking into account the pa_threshold (field rotation).

    Parameters
    ----------
    cube : array_like, 3d
        Input cube.
    angle_list : array_like, 1d
        Corresponding parallactic angle for each frame.
    fwhm : float
        Known size of the FHWM in pixels to be used. Default is 4.
    radius_int : int, optional
        The radius of the innermost annulus. By default is 0, if >0 then the
        central circular area is discarded.
    asize : int, optional
        The size of the annuli, in FWHM. Default is 2.
    delta_rot : int, optional
        Factor for increasing the parallactic angle threshold, expressed in FWHM.
        Default is 1 (excludes 1 FHWM on each side of the considered frame).
    mode : {"fullfr","annular"}, str optional
        In "simple" mode only the median frame is subtracted, in "annular" mode
        also the 4 closest frames given a PA threshold (annulus-wise) are
        subtracted.
    nframes : even int optional
        Number of frames to be used for building the optimized reference PSF
        when working in annular mode.
    collapse : {'median', 'mean', 'sum', 'trimmean'}, str optional
        Sets the way of collapsing the frames for producing a final image.
    full_output: boolean, optional
        Whether to return the final median combined image only or with other
        intermediate arrays.
    verbose : {True, False}, bool optional
        If True prints to stdout intermediate info.

    Returns
    -------
    frame : array_like, 2d
        Median combination of the de-rotated cube.
    If full_output is True:
    cube_out : array_like, 3d
        The cube of residuals.
    cube_der : array_like, 3d
        The derotated cube of residuals.

    """
    def find_indices(angle_list, frame, thr, nframes):
        """ Returns the indices to be left in frames library for optimized ADI.
        To find a more pythonic way to do this!
        """
        n = angle_list.shape[0]
        index_prev = 0
        index_foll = frame
        for i in range(0, frame):
            if np.abs(angle_list[frame]-angle_list[i]) < thr:
                index_prev = i
                break
            else:
                index_prev += 1
        for k in range(frame, n):
            if np.abs(angle_list[k]-angle_list[frame]) > thr:
                index_foll = k
                break
            else:
                index_foll += 1

        window = int(nframes/2)
        ind1 = index_prev-window
        ind1 = max(ind1, 0)
        ind2 = index_prev
        ind3 = index_foll
        ind4 = index_foll+window
        ind4 = min(ind4, n)
        indices = np.array(range(ind1,ind2)+range(ind3,ind4))
        #print ind1, ind2, ind3, ind4, indices
        return indices

    #***************************************************************************
    array = cube

    if not array.ndim == 3:
        raise TypeError('Input array is not a cube or 3d array.')
    if not array.shape[0] == angle_list.shape[0]:
        raise TypeError('Input vector or parallactic angles has wrong length.')
    if not nframes%2==0:
        raise TypeError('nframes argument must be even value.')

    n, y, _ = array.shape

    if verbose:  start_time = time_ini()

    angle_list = check_PA_vector(angle_list)

    #***************************************************************************
    # The median frame (basic psf reference) is first subtracted from each frame.
    #***************************************************************************
    ref_psf = np.median(array, axis=0)
    array = array - ref_psf

    if mode=='fullfr':
        if radius_int>0:
            cube_out = mask_circle(array, radius_int)
        else:
            cube_out = array
        if verbose:  print 'Median psf reference subtracted'

    elif mode=='annular':
        annulus_width = int(asize * fwhm)                            # equal size for all annuli
        n_annuli = int(np.floor((y/2-radius_int)/annulus_width))
        if verbose:  print 'N annuli =', n_annuli, ', FWHM =', fwhm, '\n'
        #***********************************************************************
        # The annuli are built, and the corresponding PA thresholds for frame
        # rejection are calculated. The PA rejection is calculated at center of
        # the annulus.
        #***********************************************************************
        cube_out = np.zeros_like(array)
        for ann in range(n_annuli):
            pa_threshold,inner_radius,_= define_annuli(angle_list, ann, n_annuli,
                                                       fwhm, radius_int,
                                                       annulus_width, delta_rot,
                                                       verbose)

            indices = get_annulus(array[0], inner_radius, annulus_width,
                                  output_indices=True)
            yy = indices[0]
            xx = indices[1]

            matrix = array[:, yy, xx]    # shape [nframes x npx_annulus]

            #*******************************************************************
            # A second optimized psf reference is subtracted from each frame.
            # For each frame we find *nframes*, depending on the PA threshold,
            # to construct this optimized psf reference.
            #*******************************************************************
            for frame in range(n):
                if pa_threshold != 0:
                    indices_left = find_indices(angle_list, frame, pa_threshold,
                                                nframes)
                    matrix_disc = matrix[indices_left]
                else:
                    matrix_disc = matrix

                ref_psf_opt = np.median(matrix_disc, axis=0)
                curr_frame = matrix[frame]
                subtracted = curr_frame - ref_psf_opt
                cube_out[frame][yy, xx] = subtracted
        if verbose:  print 'Optimized median psf reference subtracted'

    else:
        raise RuntimeError('Mode not recognized')

    cube_der = cube_derotate(cube_out, angle_list)
    frame = cube_collapse(cube_der, mode=collapse)
    if verbose:
        print 'Done derotating and combining'
        timing(start_time)
    if full_output:
        return cube_out, cube_der, frame
    else:
        return frame


import torch
import torch.nn as nn
import numpy as np

from crazycar.algos_torch.common import Replay

class BaseNetwork(nn.Module):
    def __init__(self):
        super().__init__()

    def soft_update(self, other_network, tau):

        other_variables = other_network.parameters()
        current_variables = self.parameters()

        with torch.no_grad():
            for (current_var, other_var) in zip(current_variables, other_variables):
                current_var.data.copy_(tau * other_var.data + (1.0 - tau) * current_var.data)

    def hard_update(self, other_network):
        self.soft_update(other_network, tau=1.)

class BaseModel:
    """
    Base class for Actor-Critic algorithm
    """

    def __init__(self, replay_size=int(1e5)):
        super().__init__()
        self.rb = Replay(replay_size)

    def write_metric(self, metric, step):
        raise NotImplementedError

    def actor_loss(self, batch):
        raise NotImplementedError

    def critic_loss(self, batch):
        raise NotImplementedError

    @staticmethod
    def rescale(act, low=0, high=1):
        """
        Rescale action from tanh [-1, 1] to [low, high]
        """

        act = low + (0.5 * (act + 1.0) * (high - low))

        return act

    @staticmethod
    def _update(opt, loss_fn, batch):
        # calculate loss
        loss = loss_fn(batch)

        # update
        opt.zero_grad()
        loss.backward()
        opt.step()

        return loss

    def update_params(self, i, batch_size=256):
        batch = self.rb.sample(batch_size)

        critic_loss = self._update(self.critic_opt, self.critic_loss, batch)
        actor_loss = self._update(self.actor_opt, self.actor_loss, batch)

        # update target network
        if i % self.interval_target == 0:
            self.actor_target.soft_update(self.actor, self.tau)
            self.critic_target.soft_update(self.critic, self.tau)

        return {
            "actor_loss": actor_loss.numpy(),
            "critic_loss": critic_loss.numpy()
        }

    def random_action(self):
        act = np.random.uniform(-1, 1, size=(1, self.actor.act_dim))
        if self.actor.act_dim == 2:
            # apply rescale to speed
            act[0][0] = self.rescale(act[0][0])
        return act

    def predict(self, obs):
        act = self.actor(obs)
        act = act.numpy()
        if len(act[0]) == 2:
            # apply rescale to speed
            act[0][0] = self.rescale(act[0][0])
        return act


import numpy as np
import sys

import tensorflow as tf
from keras.backend.tensorflow_backend import set_session
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.01
set_session(tf.Session(config=config))

from keras.models import Sequential, Model
from keras.layers import Input, Embedding, LSTM, GRU, SimpleRNN, Dense, Lambda
import keras.backend as K
from keras.callbacks import ModelCheckpoint
import keras.layers as layers

from keras import optimizers
adam_half = optimizers.Adam(lr=0.0005)

from keras.preprocessing import sequence
from keras.layers.core import Dense, Dropout, Activation, Flatten, Reshape
from keras.layers.embeddings import Embedding

from random import random
from numpy import array
from numpy import cumsum
from keras.layers import TimeDistributed
from keras.layers import Bidirectional
from keras.callbacks import ModelCheckpoint
from keras.layers.normalization import BatchNormalization

print('\n\n\n\n\n\n\n\n\n\n\n')

print('###############################################################\n#                                                             #\n# Demonstration : Real-time Automatic Word Segmentation (K/E) #\n#                                                             #\n###############################################################')

import fasttext
import re

print('\nImporting dictionaries...')

dic_kor = fasttext.load_model('vectors/model_kor.bin')
def loadvector(File):
    f = open(File,'r')
    model = {}
    for line in f:
        splitLine = line.split()
        word = splitLine[0]
        embedding = np.array([float(val) for val in splitLine[1:]])
        model[word] = embedding
    return model
dic_eng = loadvector('vectors/model_eng.txt')

import string
idchar = {}
for i in range(len(string.ascii_lowercase)):
  idchar.update({string.ascii_lowercase[i]:i})

for i in range(10):
  idchar.update({i:i+26})

idchar.update({'#':36})

big  = re.compile(r"[A-Z]")
small= re.compile(r"[a-z]")
num  = re.compile(r"[0-9]")

print('Loading models...')

from keras.models import load_model
model_kor = load_model('model/model_kor.hdf5')
model_eng = load_model('model/model_eng.hdf5')

print('\nEnter "bye" to quit\n')

## Functions_KOR

threshold_kor=0.5
overlap=30

def pred_correction_rnn(sent,model,dic,maxlen,wdim):
    conv = np.zeros((1,maxlen,wdim,1))
    rnn  = np.zeros((1,maxlen,wdim))
    charcount = -1
    for j in range(len(sent)):
      if j<maxlen and sent[j]!=' ':
        charcount=charcount+1
        conv[0][charcount,:,0]=dic[sent[j]]
        rnn[0][charcount,:]=dic[sent[j]]
    z = model.predict([conv,rnn])[0]
    sent_raw = ''
    count_char=-1
    lastpoint=-1
    lastchar=-1
    for j in range(len(sent)):
      if sent[j]!=' ':
        count_char=count_char+1
        sent_raw = sent_raw+sent[j]
        if z[count_char]>threshold_kor:
          sent_raw = sent_raw+' '
          if j<overlap:
            lastpoint=len(sent_raw)
            lastchar=j
    return sent_raw, lastpoint, lastchar

def kor_spacing(s):
    if len(s)<overlap:
      temp,lp,lc = pred_correction_rnn(s,model_kor,dic_kor,100,100)
      z = temp+"\n"
    else:
      z=''
      start=0
      while start<len(s):
        if start+overlap<len(s):
          temp,lp,lc =pred_correction_rnn(s[start:start+2*overlap],model_kor,dic_kor,100,100)
          temp=temp[:lp]
        else:
          temp,lp,lc =pred_correction_rnn(s[start:],model_kor,dic_kor,100,100)
          lc = overlap
        z = z+temp
        start=start+lc+1
      z = z+"\n"
    print('>> Output:',z)
    return z

## Function_ENG

def underscore(hashtag):
    result=''
    for i in range(len(hashtag)):
      if i>0:
        if hashtag[i].isalpha()==True:
          result = result+hashtag[i]
        else:
          result = result+' '
    return result

def split_hashtag(hashtagestring):
    fo = re.compile(r'#[A-Z]{2,}(?![a-z])|[A-Z][a-z]+')
    fi = fo.findall(hashtagestring)
    result = ''
    for var in fi:
        result += var + ' '
    return result

threshold=0.35

def hash_pred(sent,model,dic1,dic2,maxlen,wdim):
    conv = np.zeros((1,maxlen,wdim,1))
    rnn  = np.zeros((1,maxlen,len(dic2)))
    charcount=-1
    lastpoint=-1
    lastchar=-1
    for j in range(len(sent)):
      if charcount<maxlen-1 and sent[j]!=' ':
        charcount=charcount+1
        if sent[j] in dic1:
          conv[0][charcount,:,0]=dic1[sent[j]]
        if sent[j] in dic2:
          rnn[0][charcount,dic2[sent[j]]]=1
    z = model.predict([conv,rnn])[0]
    sent_raw = ''
    count_char=-1
    for j in range(len(sent)):
      if sent[j]!=' ':
        count_char=count_char+1
        sent_raw = sent_raw+sent[j]
        if z[count_char]>threshold:
          sent_raw = sent_raw+' '
          if j<overlap:
            lastpoint=len(sent_raw)
            lastchar=j
    return sent_raw, z[:count_char], count_char, lastpoint, lastchar

def hash_space(tag):
    tag_re = ''
    for i in range(len(tag)):
      if tag[i].isalpha() == True:
        tag_re = tag_re+tag[i].lower()
      else:
        tag_re = tag_re+tag[i]
    sent_raw, z, count_char, lastpoint, lastchar = hash_pred(tag_re,model_eng,dic_eng,idchar,100,100)
    return sent_raw, lastpoint, lastchar

def eng_spacing(s):
    if len(s)<overlap:
      temp,lp,lc = hash_space(s)
      z = temp+"\n"
    else:
      z=''
      start=0
      while start<len(s):
        if start+overlap<len(s):
          temp,lp,lc =hash_space(s[start:start+2*overlap])
          temp=temp[:lp]
        else:
          temp,lp,lc =hash_space(s[start:])
          lc = overlap
        z = z+temp
        start=start+lc+1
      z = z+"\n"
    print('>> Output:',z)
    return z

def eng_hashsegment(hashtag):
    if '_' in hashtag:
      print('>> output:',underscore(hashtag))
      return underscore(hashtag)
    else:
      if re.search(big,hashtag) and  re.search(small,hashtag):
        print('>> output:',split_hashtag(hashtag))
        return split_hashtag(hashtag)
      else:
        return eng_spacing(hashtag[1:])

print('\nEnter "k" for Korean spacing\nEnter "e" for English segmentation\nEnter "bye" to quit\n\n')

## Demonstration

def KOR():
  print('\nAutomatic Korean spacing ...\nEnter "e" to activate English segmentation\nEnter "bye" to quit\n')
  while 1:
    s = input('>> You say: ')
    if s == 'bye':
        sys.exit()
    elif s == 'e':
        ENG()
    else:
        kor_spacing(s)

def ENG():
  print('\nAutomatic English segmentation ...\nEnter "k" to activate identification mode\nEnter "bye" to quit\n')
  while 1:
    s = input('>> You say: ')
    if s == 'bye':
        sys.exit()
    elif s == 'k':
        KOR()
    else:
        eng_spacing(s)

while 1:
  s = input(' Choose: ')
  if s == 'k':
    KOR()
  elif s == 'e':
    ENG()
  elif s == 'bye':
    sys.exit()