#!/usr/bin/env python3

import os
import os.path
import sqlite3
import sys
import datetime
import json
import xml.sax
import re
import argparse
from xml.parsers.expat import ParserCreate
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import ops

parser = argparse.ArgumentParser(description='Import an xml file into a Lexonomy database.')
parser.add_argument("-p", "--purge", help="Purge dictionary before importing [default false]", dest="purge", action="store_true")
parser.add_argument("--no-purge", dest="purge", action="store_false")
parser.set_defaults(purge=False)
parser.add_argument("-pp", "--purge-history", help="Purge the dicationary, and also purge the history. This will speed up future operations. [default false]", dest="purge_history", action="store_true")
parser.add_argument("--no-purge-history", dest="purge_history", action="store_false")
parser.set_defaults(purge_history=False)
parser.add_argument("-a", "--save-all-content", help="Also save non-entry content, such as enclosing structure and headers, etc. [default false]", dest="save_all_content", action="store_true")
parser.add_argument("--no-save-all-content", dest="save_all_content", action="store_false")
parser.set_defaults(save_all_content=False)
parser.add_argument("-u", "--user", help="Email of author (lexonomy account)", default="IMPORT@LEXONOMY")
parser.add_argument("database", help="Path to dictionary.sqlite, this should already exist.")
parser.add_argument("file", help="Path to file_to_import.xml")
args = parser.parse_args()

print("PID "+ str(os.getpid()))
print("Import started. Please wait...")

dictID = os.path.basename(args.database).replace(".sqlite", "")
db = sqlite3.connect(args.database, )
db.row_factory = sqlite3.Row
db.isolation_level=None
db.execute('PRAGMA synchronous = 0')
db.execute("PRAGMA cache_size = -100000") # about 100 megs
db.execute("begin")

historiography={"importStart": str(datetime.datetime.utcnow()), "filename": os.path.basename(args.file)}

if args.purge or args.purge_history:
    if args.purge_history:
        print("Purging history...")
        db.execute("delete from history")
    else:
        print("Copying all entries to history...")
        db.execute("insert into history(entry_id, action, [when], email, xml, historiography) select id, 'purge', ?, ?, xml, ? from entries", (str(datetime.datetime.utcnow()), args.user, json.dumps(historiography)))

    # first delete from all related tables before deleting entries themselves - immense speadup due to faster constraint checking.
    print("Purging entries...")
    db.execute("delete from sub")
    db.execute("delete from searchables")
    db.execute("delete from linkables")
    db.execute("delete from sub")
    db.execute("delete from entries")
    db.commit()
    print("Compressing database...")
    db.execute("vacuum")
    db.commit()

# By default we assume the root of the defined document structure is an entry
# and not the root of the entire dictionary.
# (otherwise that would mean your entire dictionary would be stored in 1 entry)
configs: ops.Configs = ops.readDictConfigs(db)
xema: ops.ConfigXema = configs.get("xema", {})
entryTag: str = xema.get("root", "")
rootTag: str = "" # unused apart from entry tag detection code
xmldata: bytes = open(args.file, 'rb').read() # NOTE: do not decode into string, otherwise we cannot extract the entries because byte and char offsets will mismatch. What I think happens is  sax returns byte offsets, while if we decode into string here, slices on this string will interpret indices as char offsets.

if entryTag:
    print("Entry tag from xema: " + entryTag)

# If there is no document structure defined (the xema is empty?)
# Then assume the dictionary looks like this, so detect the first child of the root, and set that as entryTag
# <dictionary> <!-- or whatever else -->
#   <entry>...</entry>
#   <entry>...</entry>
# </dictionary>
if not entryTag:
    # first pass -- check what is entry and how many entries we have
    class handlerFirst(xml.sax.ContentHandler):
        def startElement(self, name, attrs): # first element is the root, second element is the entry
            global rootTag
            global entryTag
            if rootTag == "":
                rootTag = name
            elif entryTag == "":
                entryTag = name
            else:
                pass
        def endElement(self, name):
            global entryCount
            global entryTag
            if name == entryTag:
                entryCount += 1

    # Remove leading declarations. (NOTE: ported from old code - why do we do this? does SAX report these as elements and we think they're the entry?)
    # Regex works on byte arrays, but all arguments must be bytes.
    xmldata = re.sub('<\?xml[^?]*\?>'.encode('utf-8'), b'', xmldata)
    xmldata = re.sub('<!DOCTYPE[^>]*>'.encode('utf-8'), b'', xmldata)
    try:
        saxParser = xml.sax.parseString(xmldata, handlerFirst())
    except xml.sax._exceptions.SAXParseException as e:
        if "junk after document element" in str(e):
            xmldata = "<fakeroot>"+xmldata+"</fakeroot>"
            rootTag = ""
            entryTag = ""
            entryCount = 0
            saxParser = xml.sax.parseString(xmldata, handlerFirst()) # if this errors too, just throw
        else:
            if entryTag == "":
                print("Not possible to detect element name for entry, please fix errors:")
                print(e, file=sys.stderr)
                sys.exit()

if not entryTag:
    print("Not possible to detect element name for entry, define a xema in the database, or fix errors in xml file")
    sys.exit()

p = ParserCreate("utf-8")
elementStarts: list[int] = []
addedUntil = 0
justClosedEntry = False
inEntry = False
restOfDocument: list[str] = []
entriesImported = 0
totalData = len(xmldata)

def indexEntry():
    global p
    global elementStarts
    global addedUntil
    global justClosedEntry
    global inEntry
    global restOfDocument
    global entriesImported
    if justClosedEntry:
        startOfEntry = elementStarts.pop()
        endOfEntry = p.CurrentByteIndex
        entrycontent = xmldata[startOfEntry:endOfEntry]
        justClosedEntry = False
        addedUntil = p.CurrentByteIndex

        id, xml, success, feedback = ops.createEntry(db, configs, entrycontent.decode("utf-8"), args.user)
        if success:
            entriesImported = entriesImported + 1
            if args.save_all_content:
                restOfDocument.append(f"<lxnm:entry id=\"{id}\"/>")

        percent = int(addedUntil / totalData * 100)
        if ((entriesImported % 100) == 0):
            print(f"{percent}% - {entriesImported} entries imported so far...")

def indexInBetween():
    global p
    global elementStarts
    global addedUntil
    global justClosedEntry
    global inEntry
    global restOfDocument

    # something just closed
    # when do we need to add it?
    # that's all, now to add
    if not args.save_all_content or justClosedEntry or inEntry:
        return
    content = xmldata[addedUntil:p.CurrentByteIndex]

    restOfDocument.append(content)
    addedUntil = p.CurrentByteIndex

# 3 handler functions
def start_element(name, attrs):
    global p
    global elementStarts
    global addedUntil
    global justClosedEntry
    global inEntry
    global restOfDocument
    indexEntry()
    indexInBetween()
    if name == entryTag:
        inEntry = True

    elementStarts.append(p.CurrentByteIndex)

def end_element(name):
    global p
    global elementStarts
    global addedUntil
    global justClosedEntry
    global inEntry
    global restOfDocument
    indexEntry()
    indexInBetween()
    if name == entryTag:
        justClosedEntry = True
        inEntry = False
    else:
        justClosedEntry = False
        elementStarts.pop()

def char_data(data: str):
    indexEntry()

p.StartElementHandler = start_element
p.EndElementHandler = end_element
p.CharacterDataHandler = char_data

p.Parse(xmldata)

# finished
db.commit()
db.close()
print(f"100% - {entriesImported} total entries imported.")
exit()


from functools import partial
import logging
import os
import glob

from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy_continuum.utils import version_class
from urllib import quote
from core import db
from core import Node, File
import core.config as config
from core import request_handler as _request_handler
from core.archive import get_archive_for_node
from contenttypes import Container
from contenttypes import Content
from contenttypes.data import Data
from schema.schema import existMetaField
from web.frontend.filehelpers import sendZipFile, splitpath, build_transferzip, node_id_from_req_path, split_image_path,\
    preference_sorted_image_mimetypes, version_id_from_req, get_node_or_version
from utils import userinput
import utils.utils
from utils.utils import getMimeType, clean_path, get_filesize
import tempfile
from utils.compat import iterkeys
from core import httpstatus

logg = logging.getLogger(__name__)
q = db.query

def _send_thumbnail(thumb_type, req):
    try:
        nid = node_id_from_req_path(req)
    except ValueError:
        return 400

    version_id = version_id_from_req(req)

    node_or_version = get_node_or_version(nid, version_id, Data)

    if not node_or_version.has_read_access():
        return 404

    FileVersion = version_class(File)
    if version_id:
        version = node_or_version
        files = version.files.filter_by(filetype=thumb_type, transaction_id=version.transaction_id).all()
        if not files:
            # files may be None if in this version only metadata changed
            # then try previous transaction_ids
            files = version.files.filter(FileVersion.filetype==thumb_type, FileVersion.transaction_id<=version.transaction_id). \
                order_by(FileVersion.transaction_id.desc())
        for f in files:
            if f.exists:
                return _request_handler.sendFile(req, f.abspath, f.mimetype)

        ntype, schema = version.type, version.schema
    else:
        # no version id given
        # XXX: better to use scalar(), but we must ensure that we have no dupes first
        node = node_or_version
        for f in node.files.filter_by(filetype=thumb_type):
            if f.exists:
                return _request_handler.sendFile(req, f.abspath, f.mimetype)

        try:
            ntype, schema = node.type, node.schema
        except NoResultFound:
            return 404

    for p in _request_handler.getFileStorePaths("/img/"):
        for test in ["default_thumb_%s_%s.*" % (ntype, schema),
                     "default_thumb_%s.*" % schema,
                     "default_thumb_%s.*" % ntype]:
            fps = glob.glob(os.path.join(p, test))
            if fps:
                thumb_mimetype, thumb_type = utils.utils.getMimeType(fps[0])
                return _request_handler.sendFile(req, fps[0], thumb_mimetype, force=1)

    return _request_handler.sendFile(req, config.basedir + "/web/img/questionmark.png", "image/png", force=1)

send_thumbnail = partial(_send_thumbnail, u"thumb")
send_thumbnail2 = partial(_send_thumbnail, u"presentation")

def _send_file_with_type(filetype, mimetype, req, checkonly=False):
    try:
        nid = node_id_from_req_path(req)
    except ValueError:
        return 400

    version_id = version_id_from_req(req)
    node = get_node_or_version(nid, version_id, Content)

    if node is None or not node.has_data_access():
        return 404

    fileobj = None
    file_query = node.files.filter_by(filetype=filetype)
    # if version_id == u"published":
    if version_id:
        file_query = file_query.filter_by(transaction_id=node.transaction_id)
        fileobj = file_query.scalar()
        # fileobj may be None if in this version only metadata changed
        # then try previous transaction_ids
        if not fileobj:
            FileVersion = version_class(File)
            # this a long lasting query
            file_query = node.files.filter_by(filetype=filetype)
            fileobj = file_query.filter(FileVersion.transaction_id <= node.transaction_id).\
                order_by(FileVersion.transaction_id.desc()).first()
    if mimetype:
        file_query = file_query.filter_by(mimetype=mimetype)

    if not fileobj:
        fileobj = file_query.scalar()
    if fileobj is not None:
        if checkonly:
            return 200
        return _request_handler.sendFile(req, fileobj.abspath, fileobj.mimetype)

    return 404

send_doc = partial(_send_file_with_type, u"document", None)

def send_image(req):
    try:
        nid, file_ext = split_image_path(req.path)
    except ValueError:
        return 400

    version_id = version_id_from_req(req)

    node = get_node_or_version(nid, version_id, Content)

    # XXX: should be has_data_access instead, see #1135
    if node is None or not node.has_read_access():
        return 404

    image_files_by_mimetype = {f.mimetype: f for f in node.files.filter_by(filetype=u"image")}

    if not image_files_by_mimetype:
        # no image files? forget it...
        return 404

    def _send(fileobj):
        return _request_handler.sendFile(req, fileobj.abspath, fileobj.mimetype)

    client_mimetype = None

    if file_ext:
        # client wants a specific mimetype
        client_mimetype = node.MIMETYPE_FOR_EXTENSION.get(file_ext)
        if not client_mimetype:
            return httpstatus.HTTP_NOT_ACCEPTABLE

        image_file = image_files_by_mimetype.get(client_mimetype)
        if image_file:
            return _send(image_file)
        else:
            return httpstatus.HTTP_NOT_ACCEPTABLE

    # figure out what we want to send, in that order:
    server_preferred_mimetypes = preference_sorted_image_mimetypes(node, iterkeys(image_files_by_mimetype))

    accept_mimetypes = _request_handler.accept_mimetypes(req)

    if accept_mimetypes:
        client_mimetype = accept_mimetypes.best_match(server_preferred_mimetypes)
        if client_mimetype:
            # file for mimetype must exist here
            image_file = image_files_by_mimetype[client_mimetype]
            return _send(image_file)
        else:
            return httpstatus.HTTP_NOT_ACCEPTABLE
    else:
        # client doesn't have any preferences, send our choice
        return _send(image_files_by_mimetype[server_preferred_mimetypes[0]])

    return 404

def send_original_file(req):
    try:
        nid = node_id_from_req_path(req)
    except ValueError:
        return 400

    version_id = version_id_from_req(req)

    node = get_node_or_version(nid, version_id, Data)

    if node is None or not node.has_data_access():
        return 404

    original_filetype = node.get_original_filetype()
    original_file = node.files.filter_by(filetype=original_filetype).scalar()
    if original_file is not None:
        return _request_handler.sendFile(req, original_file.abspath, original_file.mimetype)

    return 404

def send_file(req):
    parts = splitpath(req.path)
    if len(parts) != 2:
        return 400

    nidstr, filename = parts
    if nidstr.endswith("_transfer.zip"):
        nidstr = nidstr[:-13]

    nid = userinput.string_to_int(nidstr)
    if nid is None:
        return 400

    version_id = version_id_from_req(req)

    node = get_node_or_version(nid, version_id)

    if (node is None
            or isinstance(node, Container) and not node.has_read_access()
            or isinstance(node, Content) and not node.has_data_access()):
        return 404

    def _send_attachment(filepath, mimetype):
        file_ext = os.path.splitext(filepath)[1]
        if existMetaField(node.schema, u'nodename'):
            display_file_name = u'{}{}'.format(os.path.splitext(os.path.basename(node.name))[0], file_ext)
        else:
            display_file_name = filename
        try:
            display_file_name.encode('ascii')
        except UnicodeEncodeError:
            req.reply_headers["Content-Disposition"] = u'attachment; filename="{0}"; filename*=UTF-8\'\'{0}'.\
                format(quote(display_file_name.encode('utf8')))
        else:
            req.reply_headers["Content-Disposition"] = u'attachment; filename="{}"'.format(display_file_name)
        return _request_handler.sendFile(req, filepath, mimetype)

    if filename is None:
        # build zip-file and return it
        with tempfile.NamedTemporaryFile() as tmpfile:
            files_written = build_transferzip(tmpfile, node)
            if files_written == 0:
                return 404
            # don't enable nginx x_accel_redirect for temporary files
            return _request_handler.sendFile(req, tmpfile.name, "application/zip", nginx_x_accel_redirect_enabled=False)

    # try full filename
    for f in node.files:
        if f.base_name == filename:
            return _send_attachment(f.abspath, f.mimetype)

    archive = get_archive_for_node(node)
    if archive:
        filepath = archive.get_local_filepath(node)
        mimetype, _ = getMimeType(filepath)
        return _send_attachment(filepath, mimetype)

    else:
        # try only extension
        file_ext = os.path.splitext(filename)[1]
        for f in node.files:
            if os.path.splitext(f.base_name)[1] == file_ext and f.filetype in [u'document', u'original', u'mp3']:
                logg.warn("serving file %s for node %s only by matched extension", f.path, node.id)
                return _send_attachment(f.abspath, f.mimetype)

    return 404

def send_attachment(req):
    try:
        nid = node_id_from_req_path(req)
        version_id = version_id_from_req(req)
    except ValueError:
        return 400

    node = get_node_or_version(nid, version_id, Data)

    if (node is None
            or isinstance(node, Container) and not node.has_read_access()
            or isinstance(node, Content) and not node.has_data_access()):
        return 404

    attachment_file = node.files.filter_by(filetype=u"attachment").first()
    if attachment_file is not None:
        # filename is attachment.zip
        sendZipFile(req, file.abspath)

def send_attfile(req):
    """send single attachment file to user"""
    parts = req.path[9:].split('/')

    if len(parts) < 2:
        return 400

    nid = userinput.string_to_int(parts[0])
    if nid is None:
        return 400

    version_id = version_id_from_req(req)

    node = get_node_or_version(nid, version_id, Data)

    # XXX: why do we want to send attachments from containers?
    if (node is None
            or isinstance(node, Container) and not node.has_read_access()
            or isinstance(node, Content) and not node.has_data_access()):
        return 404

    paths = ["/".join(parts[1:]), "/".join(parts[1:-1])]
    fileobjs = [fo for fo in node.files if fo.path in paths]

    if not fileobjs:
        return 404

    fileobj = fileobjs[0]

    if fileobj.mimetype == u'inode/directory':
        # files in attachment directory cannot be found in node.files
        # so send file directly as it was made in mysql
        filename = clean_path("/".join(parts[1:]))
        path = os.path.join(config.get("paths.datadir"), filename)
        mime, type = getMimeType(filename)
        if (get_filesize(filename) > 16 * 1048576):
            req.reply_headers["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)

        return _request_handler.sendFile(req, path, mime)

    if (fileobj.size > 16 * 1048576):
        req.reply_headers["Content-Disposition"] = u'attachment; filename="{}"'.format(fileobj.base_name).encode('utf8')

    return _request_handler.sendFile(req, fileobj.abspath, fileobj.mimetype)

def fetch_archived(req):
    try:
        nid = node_id_from_req_path(req)
    except ValueError:
        return 400

    node = q(Content).get(nid)

    archive = get_archive_for_node(node)
    if archive:
        try:
            archive.fetch_file_from_archive(node)
        except:
            logg.exception("exception in fetch_file_from_archive for archive %s", archive.archive_type)
            msg = "fetch archive for node failed"
            req.setStatus(500)
            req.write(msg)
        else:
            req.write('done')
    else:
        msg = "archive for node not found"
        req.setStatus(404)
        req.write(msg)
        logg.warn(msg)

    db.session.commit()

def send_from_webroot(req):
    import core.webconfig

    if core.webconfig.theme is not None:
        webroot_dirs = [core.webconfig.theme.path, "web/root"]
    else:
        webroot_dirs = ["web/root"]

    for webroot_dir in webroot_dirs:
        filepath = os.path.join(config.basedir, webroot_dir, req.path.strip("/"))
        if os.path.isfile(filepath):
            return _request_handler.sendFile(req, filepath, getMimeType(filepath)[0])

    return 404

### redirects for legacy handlers

def redirect_images(req):
    req.reply_headers["Location"] = "/image" + req.uri[7:]
    return 301


import sys

FILENAME = sys.argv[1]
TARGET = sys.argv[2]

OPERATIONS = {}  # In the form of {'letter': [var1, operator, var2]} where [] can vary
RESULTS = {}  # In the form of {'letter': 'result'}

def find(target):
    if target.isdigit():  # Covers the case the target comes from an operation but it is an integer
        return int(target)

    if target in RESULTS:  # Speeds up and avoids a potential circular recursion call
        return RESULTS[target]

    try:
        operation = OPERATIONS[target]
    except KeyError:
        print(f'Something is wrong with the input. {target} does not exist')
        sys.exit(0)

    if len(operation) == 1:
        try:
            RESULTS[target] = int(operation[0])  # We got to an end with an integer...
        except ValueError:
            return find(operation[0])  # ...or we found another non-numerical element

    elif len(operation) == 2:
        # Python's NOT bitwise operator is two's complement which means it's gonna
        # return -n - 1, or a signed integer (~4 will be -5, so a 4 bit representation
        # or 2^4, allows us to write 16 ints from -8 to 7).
        # What we want to achieve is the unsigned int, which goes from
        # 0 to 15 so we need to mask it with 0xFFFF (4 bits for each hexa, so 16 in total
        # or 2^16 which is 65535). We are doing the negation of N, or 65535 - N. So NOT 1 = 65534, NOT 2 = 65533...
        RESULTS[target] = ~find(operation[1]) & 0xFFFF
    else:
        operator = operation[1]

        if operator == 'AND':
            res = find(operation[0]) & find(operation[2])
        elif operator == 'OR':
            res = find(operation[0]) | find(operation[2])
        elif operator == 'RSHIFT':
            res = find(operation[0]) >> int(operation[2])
        elif operator == 'LSHIFT':
            res = find(operation[0]) << int(operation[2])

        RESULTS[target] = res

    OPERATIONS.pop(target)

    return RESULTS[target]

if __name__ == '__main__':

    with open(FILENAME, 'r') as infile:
        for line in infile.read().splitlines():
            op, letter = line.split(' -> ')
            OPERATIONS[letter] = op.split(' ')

    print(find(TARGET))


#!/usr/bin/python3
"""prints the State object with the name passed as argument from the db"""

if __name__ == "__main__":
    import sys
    from model_state import Base, State
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy import (create_engine)

    engine = create_engine(
            'mysql+mysqldb://{}:{}@localhost/{}'.format
            (sys.argv[1], sys.argv[2], sys.argv[3]),
            pool_pre_ping=True)

    Session = sessionmaker(bind=engine)
    session = Session()

    sql = session.query(State).filter(State.name == sys.argv[4]).all()

    if sql != []:
        for row in sql:
            print(row.id)
    else:
        print("Not found")


"""External drives to network."""

# Authors: Mainak Jas <mjas@mgh.harvard.edu>
#          Sam Neymotin <samnemo@gmail.com>
#          Christopher Bailey <bailey.cj@gmail.com>

import numpy as np

from .params import (_extract_bias_specs_from_hnn_params,
                     _extract_drive_specs_from_hnn_params)

def _get_target_properties(weights_ampa, weights_nmda, synaptic_delays,
                           location, probability=1.0):
    """Retrieve drive properties associated with each target cell type

    Note that target cell types of a drive are inferred from the synaptic
    weight and delay parameters keys defined by the user.
    """

    # allow passing weights as None, but make iterable here
    if weights_ampa is None:
        weights_ampa = dict()
    if weights_nmda is None:
        weights_nmda = dict()

    weights_by_type = {cell_type: dict() for cell_type in
                       (set(weights_ampa.keys()) | set(weights_nmda.keys()))}
    for cell_type in weights_ampa:
        weights_by_type[cell_type].update({'ampa': weights_ampa[cell_type]})
    for cell_type in weights_nmda:
        weights_by_type[cell_type].update({'nmda': weights_nmda[cell_type]})

    target_populations = set(weights_by_type)
    if not target_populations:
        raise ValueError('No target cell types have been given a synaptic '
                         'weight for this drive.')
    # Distal drives should not target L5 basket cells according to the
    # canonical Jones model
    if location == 'distal' and 'L5_basket' in target_populations:
        raise ValueError('Due to physiological/anatomical constraints, '
                         'a distal drive cannot target L5_basket cell types. '
                         'L5_basket cell types must remain undefined by '
                         'the user in all synaptic weights dictionaries '
                         'for this drive. '
                         'Therefore, please remove the L5_basket entries '
                         'from the corresponding dictionaries.')

    if isinstance(synaptic_delays, float):
        delays_by_type = {cell_type: synaptic_delays for cell_type in
                          target_populations}
    else:
        delays_by_type = synaptic_delays.copy()

    if set(delays_by_type.keys()) != target_populations:
        raise ValueError('synaptic_delays is either a common float or needs '
                         'to be specified as a dict for each of the cell '
                         'types defined in weights_ampa and weights_nmda '
                         f'({target_populations})')

    if isinstance(probability, float):
        probability_by_type = {cell_type: probability for cell_type in
                               target_populations}
    else:
        probability_by_type = probability.copy()

    if set(probability_by_type.keys()) != target_populations:
        raise ValueError('probability is either a common float or needs '
                         'to be specified as a dict for each of the cell '
                         'types defined in weights_ampa and weights_nmda '
                         f'({target_populations})')

    return (target_populations, weights_by_type, delays_by_type,
            probability_by_type)

def _check_drive_parameter_values(drive_type, **kwargs):
    if 'tstop' in kwargs:
        if kwargs['tstop'] is not None:
            if kwargs['tstop'] < 0.:
                raise ValueError(f'End time of {drive_type} drive cannot be '
                                 'negative')
            if 'tstart' in kwargs and kwargs['tstop'] < kwargs['tstart']:
                raise ValueError(f'Duration of {drive_type} drive cannot be '
                                 'negative')
    if 'sigma' in kwargs:
        if kwargs['sigma'] < 0.:
            raise ValueError('Standard deviation cannot be negative')
    if 'numspikes' in kwargs:
        if not kwargs['numspikes'] > 0:
            raise ValueError('Number of spikes must be greater than zero')
    if 'tstart' in kwargs:
        if kwargs['tstart'] < 0:
            raise ValueError(f'Start time of {drive_type} drive cannot be '
                             'negative')

    if ('numspikes' in kwargs and 'spike_isi' in kwargs and
            'burst_rate' in kwargs):
        n_spikes = kwargs['numspikes']
        isi = kwargs['spike_isi']
        burst_period = 1000. / kwargs['burst_rate']
        burst_duration = (n_spikes - 1) * isi
        if burst_duration > burst_period:
            raise ValueError(f'Burst duration ({burst_duration}s) cannot'
                             f' be greater than burst period ({burst_period}s)'
                             'Consider increasing the spike ISI or burst rate')

def _check_poisson_rates(rate_constant, target_populations, all_cell_types):
    if isinstance(rate_constant, dict):
        constants_provided = set(rate_constant.keys())
        if not target_populations.issubset(constants_provided):
            raise ValueError(
                f"Rate constants not provided for all target cell "
                f"populations ({target_populations})")
        if not constants_provided.issubset(all_cell_types):
            offending_keys = constants_provided.difference(all_cell_types)
            raise ValueError(
                f"Rate constant provided for unknown target cell "
                f"population: {offending_keys}")
        for key, val in rate_constant.items():
            if not val > 0.:
                raise ValueError(
                    f"Rate constant must be positive ({key}, {val})")
    else:
        if not rate_constant > 0.:
            raise ValueError(
                f"Rate constant must be positive, got {rate_constant}")

def _add_drives_from_params(net):
    drive_specs = _extract_drive_specs_from_hnn_params(
        net._params, list(net.cell_types.keys()), net._legacy_mode)
    bias_specs = _extract_bias_specs_from_hnn_params(
        net._params, list(net.cell_types.keys()))

    for drive_name in sorted(drive_specs.keys()):  # order matters
        specs = drive_specs[drive_name]
        if specs['type'] == 'evoked':
            net.add_evoked_drive(
                drive_name, mu=specs['dynamics']['mu'],
                sigma=specs['dynamics']['sigma'],
                numspikes=specs['dynamics']['numspikes'],
                n_drive_cells=specs['dynamics']['n_drive_cells'],
                cell_specific=specs['cell_specific'],
                weights_ampa=specs['weights_ampa'],
                weights_nmda=specs['weights_nmda'],
                location=specs['location'], event_seed=specs['event_seed'],
                synaptic_delays=specs['synaptic_delays'],
                space_constant=specs['space_constant'])
        elif specs['type'] == 'poisson':
            net.add_poisson_drive(
                drive_name, tstart=specs['dynamics']['tstart'],
                tstop=specs['dynamics']['tstop'],
                rate_constant=specs['dynamics']['rate_constant'],
                weights_ampa=specs['weights_ampa'],
                weights_nmda=specs['weights_nmda'],
                location=specs['location'], event_seed=specs['event_seed'],
                synaptic_delays=specs['synaptic_delays'],
                space_constant=specs['space_constant'])
        elif specs['type'] == 'gaussian':
            net.add_evoked_drive(  # 'gaussian' is just evoked
                drive_name, mu=specs['dynamics']['mu'],
                sigma=specs['dynamics']['sigma'],
                numspikes=specs['dynamics']['numspikes'],
                weights_ampa=specs['weights_ampa'],
                weights_nmda=specs['weights_nmda'],
                location=specs['location'], event_seed=specs['event_seed'],
                synaptic_delays=specs['synaptic_delays'],
                space_constant=specs['space_constant'])
        elif specs['type'] == 'bursty':
            net.add_bursty_drive(
                drive_name,
                tstart=specs['dynamics']['tstart'],
                tstart_std=specs['dynamics']['tstart_std'],
                tstop=specs['dynamics']['tstop'],
                burst_rate=specs['dynamics']['burst_rate'],
                burst_std=specs['dynamics']['burst_std'],
                numspikes=specs['dynamics']['numspikes'],
                spike_isi=specs['dynamics']['spike_isi'],
                n_drive_cells=specs['dynamics']['n_drive_cells'],
                cell_specific=specs['cell_specific'],
                weights_ampa=specs['weights_ampa'],
                weights_nmda=specs['weights_nmda'],
                location=specs['location'],
                space_constant=specs['space_constant'],
                synaptic_delays=specs['synaptic_delays'],
                event_seed=specs['event_seed'])

    # add tonic biases if present in params
    for cellname in bias_specs['tonic']:
        net.add_tonic_bias(
            cell_type=cellname,
            amplitude=bias_specs['tonic'][cellname]['amplitude'],
            t0=bias_specs['tonic'][cellname]['t0'],
            tstop=bias_specs['tonic'][cellname]['tstop'])

    # in HNN-GUI, seed is determined by "absolute GID" instead of the
    # gid offset with respect to the first cell of a population.
    for drive_name, drive in net.external_drives.items():
        drive['event_seed'] += net.gid_ranges[drive_name][0]

def _get_prng(seed, gid):
    """Random generator for this instance.

    Parameters
    ----------
    seed : int
        The seed for random state generator.
    gid : int
        The cell ID
    sync_evinput : bool
        If True, all cells get the same prng

    Returns
    -------
    prng : instance of RandomState
        The seed for events assuming a given start time; dependent on gid.
    prng2 : instance of RandomState
        The seed for generating randomized start times.
        Used in _create_bursty_input
    """
    return np.random.RandomState(seed + gid), np.random.RandomState(seed)

def _drive_cell_event_times(drive_type, dynamics, tstop, target_type='any',
                            trial_idx=0, drive_cell_gid=0, event_seed=0):
    """Generate event times for one artificial drive cell based on dynamics.

    Parameters
    ----------
    drive_type : str
        The drive type, which is one of
        'poisson' : Poisson-distributed dynamics from t0 to T
        'gaussian' : Gaussian-distributed dynamics from t0 to T
        'evoked' : Spikes occur at specified time (mu) with dispersion (sigma)
        'bursty' : Spikes occur in bursts (events_per_cycle spikes at
        cycle_events_isi intervals) in a rhythmic (f_input) fashion
    dynamics : dict
        Parameters of the event time dynamics to simulate
    tstop : float
        The simulation stop time (ms).
    target_type : str
        Type of cell (e.g. 'L2_basket') this drive cell will target. If
        'any' (default), the drive cell is non-specific.
    trial_idx : int
        The index number of the current trial of a simulation (default=1).
    drive_cell_gid : int
        Optional gid of current artificial cell (used for seeding)
    event_seed : int
        Optional initial seed for random number generator.

    Returns
    -------
    event_times : list
        The event times at which spikes occur.
    """
    prng, prng2 = _get_prng(seed=event_seed + trial_idx,
                            gid=drive_cell_gid)

    # check drive name validity, allowing substring matches
    valid_drives = ['evoked', 'poisson', 'gaussian', 'bursty']
    # NB check if drive_type has a valid substring, not vice versa
    matches = [f for f in valid_drives if f in drive_type]
    if len(matches) == 0:
        raise ValueError('Invalid external drive: %s' % drive_type)
    elif len(matches) > 1:
        raise ValueError('Ambiguous external drive: %s' % drive_type)

    event_times = np.array([])
    if drive_type == 'poisson':
        if target_type == 'any':
            rate_constant = dynamics['rate_constant']
        elif target_type in dynamics['rate_constant']:
            rate_constant = dynamics['rate_constant'][target_type]
        # XXX required for legacy mode since drive cells are created in network
        # for which rate constant may not be defined
        if target_type == 'any' or target_type in dynamics['rate_constant']:
            event_times = _create_extpois(
                t0=dynamics['tstart'],
                T=dynamics['tstop'],
                lamtha=rate_constant,
                prng=prng)
    elif drive_type == 'evoked' or drive_type == 'gaussian':
        event_times = _create_gauss(
            mu=dynamics['mu'],
            sigma=dynamics['sigma'],
            numspikes=dynamics['numspikes'],
            prng=prng)
    elif drive_type == 'bursty':
        event_times = _create_bursty_input(
            t0=dynamics['tstart'],
            t0_stdev=dynamics['tstart_std'],
            tstop=dynamics['tstop'],
            f_input=dynamics['burst_rate'],
            events_jitter_std=dynamics['burst_std'],
            events_per_cycle=dynamics['numspikes'],
            cycle_events_isi=dynamics['spike_isi'],
            prng=prng,
            prng2=prng2)

    # brute force remove non-zero times. Might result in fewer vals
    # than desired
    # values MUST be sorted for VecStim()!
    event_times = event_times[np.logical_and(event_times > 0,
                                             event_times <= tstop)]
    event_times.sort()
    event_times = event_times.tolist()

    return event_times

def _create_extpois(*, t0, T, lamtha, prng):
    """Create poisson inputs.

    Parameters
    ----------
    t0 : float
        The start time (in ms).
    T : float
        The end time (in ms).
    lamtha : float
        The rate parameter for spike train (in Hz)
    prng : instance of RandomState
        The random state.

    Returns
    -------
    event_times : array
        The event times.
    """
    # see: http://www.cns.nyu.edu/~david/handouts/poisson.pdf
    if t0 < 0:
        raise ValueError('The start time for Poisson inputs must be'
                         f'greater than 0. Got {t0}')
    if T < t0:
        raise ValueError('The end time for Poisson inputs must be'
                         f'greater than start time. Got ({t0}, {T})')
    if lamtha <= 0.:
        raise ValueError(f'Rate must be > 0. Got {lamtha}')

    event_times = list()
    t_gen = t0
    while t_gen < T:
        t_gen += prng.exponential(1. / lamtha) * 1000.
        if t_gen < T:
            event_times.append(t_gen)

    return np.array(event_times)

def _create_gauss(*, mu, sigma, numspikes, prng):
    """Create gaussian inputs (used by extgauss and evoked).

    Parameters
    ----------
    mu : float
        The mean time of spikes.
    sigma : float
        The standard deviation.
    numspikes : float
        The number of spikes.
    prng : instance of RandomState
        The random state.

    Returns
    -------
    event_times : array
        The event times.
    """
    return prng.normal(mu, sigma, numspikes)

def _create_bursty_input(*, t0, t0_stdev, tstop, f_input,
                         events_jitter_std, events_per_cycle=2,
                         cycle_events_isi=10, prng, prng2):
    """Creates the bursty ongoing external inputs.

    Used for, e.g., for rhythmic inputs in alpha/beta generation.

    Parameters
    ----------
    t0 : float
        The start times. If -1, then randomize the start time
        of inputs uniformly between 25 ms and 125 ms.
    t0_stdev : float
        If greater than 0 and t0 != -1, randomize start time
        of inputs from a normal distribution with t0_stdev as standard
        deviation.
    tstop : float
        The stop time.
    f_input : float
        The frequency of input bursts.
    events_jitter_std : float
        The standard deviation (in ms) of each burst event.
    events_per_cycle : int
        The events per cycle. This is the spikes/burst parameter in the GUI.
        Default: 2 (doublet)
    cycle_events_isi : float
        Time between spike events within a cycle (ISI). Default: 10 ms
    prng : instance of RandomState
        The random state.
    prng2 : instance of RandomState
        The random state used for jitter to start time (see t0_stdev).

    Returns
    -------
    event_times : array
        The event times.
    """
    if t0_stdev > 0.0:
        t0 = prng2.normal(t0, t0_stdev)

    burst_period = 1000. / f_input
    burst_duration = (events_per_cycle - 1) * cycle_events_isi
    if burst_duration > burst_period:
        raise ValueError(f'Burst duration ({burst_duration}s) cannot'
                         f' be greater than burst period ({burst_period}s)'
                         'Consider increasing the spike ISI or burst rate')

    # array of mean stimulus times, starts at t0
    isi_array = np.arange(t0, tstop, burst_period)
    # array of single stimulus times -- no doublets
    t_array = prng.normal(isi_array, events_jitter_std)

    if events_per_cycle > 1:
        cycle = (np.arange(events_per_cycle) - (events_per_cycle - 1) / 2)
        t_array = np.ravel([t_array + cycle_events_isi * cyc for cyc in cycle])

    return t_array


class Node:
    def __init__(self,data):
        self.data = data
        self.left = None
        self.right = None

class BST:
    def buildbst(self, root, ele):
        if root == None:
            return Node(ele)
        if ele<root.data:
            root.left = self.buildbst(root.left, ele)
        else:
            root.right = self.buildbst(root.right, ele)
        return root

    def inorder(self, root):
        if root is None:
            return
        self.inorder(root.left)
        print(root.data)
        self.inorder(root.right)

    def iterativeinorder(self, root):
        current = root
        stack = []
        while True:
            if current is not None:
                stack.append(current)
                current = current.left
            elif stack:
                current = stack.pop()
                print(current.data)
                current = current.right
            else:
                break

root = None
b = BST()
for ele in [10, 5, 25, 2, 7, 30]:
    root = b.buildbst(root,ele)
b.inorder(root)
print("*****")
b.iterativeinorder(root)


# https://dmoj.ca/problem/ccc13s2

"""
W = int(input())
N = int(input())
total = 0
count = 0
weights = []
if N <= 4:
    for i in range(N):
        total += int(input())
        if total <= W:
            count += 1
        else:
            break
    print(count)
elif N > 4:
    for i in range(N):
        weights.append(int(input()))
    four = weights[0] + weights[1] + weights[2] + weights[3]
    if four > W:
        for i in range(4):
            total += weights[i]
            if total <= W:
                count += 1
            else:
                break
        print(count)
    else:
        for i in range(N-3):
            total = 0               #"total" should be renewed each time
            for j in range(4):
                total += weights[i+j]
            if total <= W:
                count += 1
            else:
                break
        print(count+3)
"""

W = int(input())
N = int(input())
total = 0
count = 0
weights = []
for i in range(N):
    weights.append(int(input()))
for i in range(N):
    total += weights[i]
    if i >= 4:
        total -= weights[i - 4]
    if total <= W:
        count += 1
    else:
        break
print(count)


import os
import numpy as np
import tensorflow as tf
import cv2
from tensorflow import keras
import dclab
import pandas as pd

# Setting up constants
IMG_SIZE = (250,60)
EPOCHS =20

# Load trainning set
# x - images , y - class identifier ['doublet', 'singlet']
x, y = [], []

# Iterate through each trainning datasets folder in path
train_path = 'F://RTDC//train_set'
cls_count = 0
for train_class in os.listdir(train_path):
    for file in os.listdir(os.path.join(train_path, train_class)):
        y += [cls_count]
        x += [cv2.resize(cv2.imread(os.path.join(train_path, train_class, file)),tuple(reversed(IMG_SIZE)))]
    cls_count += 1

# Convert to numpy array
x = np.array(x)
y = np.eye(cls_count)[y]

# Initializing the model
model = keras.Sequential([
    keras.layers.Conv2D(32,3, input_shape = (250, 60, 3), activation = 'relu'),
    keras.layers.Conv2D(32,3, activation = 'relu'),
    keras.layers.MaxPool2D(strides=2),
    keras.layers.Conv2D(32,3, activation = 'relu'),
    keras.layers.Conv2D(32,3, activation = 'relu'),
    keras.layers.MaxPool2D(strides=2),
    keras.layers.Conv2D(32,3, activation = 'relu'),
    keras.layers.MaxPool2D(strides=2),
    keras.layers.Flatten(),
    keras.layers.Dropout(0.25),
    keras.layers.Dense(32, activation='relu'),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dense(2, activation='softmax'),
])

# Initializing the adam optimizer
Optimizer = keras.optimizers.Adam
Opt=Optimizer(lr=1e-3, decay=1e-3 / EPOCHS)

# Compiling the model
# after trial and error with different loss functions, this one had the optimal result
model.compile(optimizer=Opt,
              loss ='categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x,y,epochs=EPOCHS)
print('End of Training')

#Load testing set

"""
# extract 900 images and write to local path using index as name"
ds = dclab.new_dataset("F://RTDC//Data001.rtdc")
imgarray = ds["image"]

for idx in range(100,1000):
    filename += [idx]
    cv2.imwrite('F://RTDC//test_set_from_rtdc//'+ str(idx) +'.png', imgarray[idx])
"""

x_test, filename = [], []

# iterate through selected test images

test_path = 'F://RTDC//test_set_from_rtdc'
# Iterate through each image-file in path
for file in os.listdir(test_path):
        filename += [file]
        x_test += [cv2.resize(cv2.imread(os.path.join(test_path, file)),tuple(reversed(IMG_SIZE)))]

# Convert to numpy array
x_test = np.array(x_test)

# predicting resuls
predictions = model.predict(x_test)
predicted_cls = np.argmax(predictions, axis=1)
confidence = np.max(predictions, axis=1)

#printing class predictions
predictions

#displaying as a pandas df where 0:doublet,1:singlet
pd.set_option('display.max_rows', 50)
df = pd.DataFrame({'Actual':filename,
 'Predicted_class':predicted_cls,
 'confidence':confidence
})

df
gfg_csv_data = df.to_csv('F://RTDC//test_set_from_rtdc//predict_result.csv', index = True)


import numpy as np
import multiprocessing as mp
from numba import njit, jit
from time import sleep, time
import os, sys

FILE_DIR = os.path.dirname(os.path.realpath(__file__))
PYTHON_DIR = f"{FILE_DIR}/../"
sys.path.insert(0, PYTHON_DIR)

from helper_functions.hdf5_handler import HDF5Handler
from helper_functions.plot_signal import plot_signal

@njit(cache=True, fastmath=True)
def process_signal(
    signal: np.ndarray,
    threshold: float,
    cutoff: int,
    packet: np.ndarray,
    carryover: int,
    packet_slack: float,
    last_signal: np.ndarray,
) -> tuple:
    """Decides if the signal is part of a packet

    Arguments:
        signal (np.ndarray): The numpy array that carries the IQ packets of the
            signal data.

        threshold (float): The minimum value that a signal must exceed to be
            considered part of a packet.

        cutoff (int): The number of IQ values that are allowed to be under the
            threshold and still be considered part of a packet.

        packet (np.ndarray): Holds the pieces of collected signals that  contain
            the packet information.

        carryover (int): If a packet is not finished being created then this
        will contain the value of the number of index's that were below the
        threshold when the signal ended (if any).

        packet_slack (int): The amount of indexes that will be added to the discoverd
            packet to ensure the whole packet is captured.

    Returns:
        tuple (list[np.ndarray], bool, int, bool):
        - list[np.ndarray]: A list containing all captured signals
        - bool: Indicates if the last packet in the list was completely captured.
        - int: Indicates how many indexes where below the threshold on the continuing packet
        - bool: Indicates if the first two packets in the list need to be concatenated.
    """
    # Skips if signal is None
    if signal == None:
        return (None, True, 0)
    # Skips if the array is empty
    if not signal.all():
        return (None, True, 0)
    last_signal = last_signal.ravel()
    if last_signal.any():
        last_signal = np.trim_zeros(last_signal)
    # Ensures array is 1D
    signal = signal.ravel()
    signal: np.ndarray = np.trim_zeros(signal)
    abs_signal: np.ndarray = np.absolute(signal)
    # Check if the max value is above the threshold
    max_value_in_signal = np.max(abs_signal)
    if max_value_in_signal < threshold:
        return (None, True, 0)
    # Each value maps to a index in abs_signal where the value is above the
    # threshold
    threshold_list: np.ndarray = np.where(abs_signal > threshold)[0]
    # Each value represents the difference between index i and i+1 in
    # threshold_list.
    threshold_diff: np.ndarray = np.diff(threshold_list)
    # Each value represents the
    cutoff_list: np.ndarray = np.where(threshold_diff > cutoff)[0]

    all_packets: list = list()
    new_carryover: int = signal.size - threshold_list[-1]
    end_of_packet_reached: bool = False
    # Checks if the current last packet does not carry over into the next signal
    if new_carryover > cutoff:
        end_of_packet_reached = True
        new_carryover = 0

    # If there is no packet and there is no cutoff found
    if packet == None:
        if cutoff_list.size == 0:
            # print("A")
            if last_signal.any():
                signal = np.concatenate((last_signal, signal))
            if threshold_list[-1] > packet_slack:
                return (
                    [signal[threshold_list[0] : threshold_list[-1] + packet_slack]],
                    end_of_packet_reached,
                    new_carryover,
                )
            else:
                return (
                    [signal[threshold_list[0] : threshold_list[-1]]],
                    end_of_packet_reached,
                    new_carryover,
                )
        for i in range(cutoff_list.size + 1):
            cutoff_list_value: int = cutoff_list[i]
            # Find the first packet
            if i == 0:
                # print("B") #! WORKING
                all_packets.append(
                    signal[
                        threshold_list[0]
                        - packet_slack : threshold_list[cutoff_list_value]
                    ]
                )
            # Find the last packet
            elif i == cutoff_list.size:
                # print("C") #! WORKING
                all_packets.append(
                    signal[
                        threshold_list[cutoff_list[-1]]
                        - packet_slack : threshold_list[-1]
                        + 1
                    ]
                )
            # Find all other packets
            else:
                # print("D") #! WORKING
                last_cutoff_list_value: int = cutoff_list[i - 1]
                all_packets.append(
                    signal[
                        threshold_list[last_cutoff_list_value + 1]
                        - packet_slack : threshold_list[cutoff_list_value]
                    ]
                )
        return (all_packets, end_of_packet_reached, new_carryover)
    else:
        if cutoff_list.size == 0 and end_of_packet_reached:
            if last_signal.any():
                signal = np.concatenate((last_signal, signal))
            # print("E")
            if threshold_list[-1] > packet_slack:
                return (
                    [signal[threshold_list[0] : threshold_list[-1] + packet_slack]],
                    end_of_packet_reached,
                    new_carryover,
                )
            else:
                if last_signal.any():
                    signal = np.concatenate((last_signal, signal))
                return (
                    [signal[threshold_list[0] : threshold_list[-1] + packet_slack]],
                    end_of_packet_reached,
                    new_carryover,
                )
        elif cutoff_list.size == 0 and not end_of_packet_reached:
            # print("F") #! WORKING
            joined_signal: np.ndarray = np.concatenate((packet, signal))
            return ([joined_signal], end_of_packet_reached, new_carryover)
        # Check if the carryover packet ended or if it continues into this signal
        packet_finished: bool = False
        if (threshold_list[0] + carryover) > cutoff:
            # print("G")
            all_packets.append(packet)
            packet_finished = True
        # Find all packets
        for i in range(cutoff_list.size + 1):
            if not cutoff_list.size > 0:
                break
            cutoff_list_value: int = cutoff_list[i]
            # Find the first packet
            if i == 0 and not packet_finished:
                # print("H")
                all_packets.append(
                    np.concatenate(
                        (packet, signal[: threshold_list[cutoff_list_value]])
                    )
                )
            elif i == 0 and packet_finished:
                # print("I")
                all_packets.append(
                    signal[threshold_list[0] : threshold_list[cutoff_list_value]]
                )
            # Find the last packet
            elif i == cutoff_list.size:
                # print("J")
                all_packets.append(
                    signal[
                        threshold_list[cutoff_list[-1]]
                        - packet_slack : threshold_list[-1]
                        + 1
                    ]
                )
            # Find all other packets
            else:
                # print("K")
                last_cutoff_list_value: int = cutoff_list[i - 1]
                all_packets.append(
                    signal[
                        threshold_list[last_cutoff_list_value + 1]
                        - packet_slack : threshold_list[cutoff_list_value]
                    ]
                )
        return (all_packets, end_of_packet_reached, new_carryover)

class PacketDetect:
    def __init__(
        self,
        stream_q: mp.Queue,
        threshold: float,
        cutoff: int,
        packet_q: mp.Queue,
        packet_slack: int,
    ):
        """A class that handles detecting when a signal is part of a packet.

        Arguments:
            stream_q (mp.Queue): A queue holding iq samples and the order in
                which they were added.

            threshold (float): The value that signifies when a signal should
                start to considered a packet.

            cutoff (int): The number of iq values that need to be below the
                threshold for the signal to be considered ended.

            packet_q (mp.Queue): A queue holding iq samples and a bool
                identifying if they are part of a packet.

            packet_slack (int): The amount of indexes that will be added to the
                discovered packet to ensure the whole packet is captured.
        """
        self.stream_q: mp.Queue = stream_q
        self.packet_q: mp.Queue = packet_q
        self.threshold: float = threshold
        self.cutoff: int = cutoff
        self.packet_slack: int = packet_slack
        self.last_signal = np.zeros(10, dtype=np.complex64)
        self.packet: np.ndarray = None
        self.carryover: int = None
        self.run = True

    def __prime_packet_detect(self) -> None:
        print("Preparing packet_detect")
        hdf5 = HDF5Handler()
        primer_signals = list()
        primer_signals.append(hdf5.get_signal("priming_signals", "signal0"))
        primer_signals.append(hdf5.get_signal("priming_signals", "signal1"))
        primer_signals.append(hdf5.get_signal("priming_signals", "signal2"))
        primer_signals.append(hdf5.get_signal("priming_signals", "signal3"))
        # Runs the jit function with dummy data to compile it before running
        # real data through it
        for signal in primer_signals:
            signal_chunks = np.array_split(signal, 40)
            for chunk in signal_chunks:
                self.stream_q.put(chunk)
        self.stream_q.put("DONE")
        self.__find_packets(True)
        # Clean out packet_q of dummy data
        while True:
            data = self.packet_q.get()
            if type(data) == str:
                if data == "DONE":
                    break
        print("packet_detect preped")

    def __find_packets(self, priming: bool = False) -> None:
        """Decides when a signal is part of a packet."""
        count = 0
        while self.run:
            count += 1
            # Display heartbeat
            if count > 5000:
                print("packet_detect still alive")
                count = 0
            # Gets the siganl data
            signal: np.ndarray = self.stream_q.get()
            start_time = time()
            # Breaks the loop
            if type(signal) == str:
                self.packet_q.put("DONE")
                break
            # Porcesses the signal
            all_packets, end_of_packet_reached, self.carryover = process_signal(
                signal,
                self.threshold,
                self.cutoff,
                self.packet,
                self.carryover,
                self.packet_slack,
                self.last_signal,
            )
            self.last_signal = signal
            if not all_packets:
                continue
            if len(all_packets) > 0 and end_of_packet_reached:
                self.packet = None
                self.carryover = 0
                self.packet_q.put(all_packets)
            elif len(all_packets) > 0 and not end_of_packet_reached:
                self.packet = all_packets.pop()
                self.packet_q.put(all_packets)
            # print(f'TIME: {time() - start_time}')
        print("EXITED PACKET_DETECT")

    def start_packet_detect(self) -> None:
        # self.__prime_packet_detect()
        self.__find_packets()

if __name__ == "__main__":
    # q_1 = mp.Queue()
    # q_2 = mp.Queue()
    # packet_d = PacketDetect(q_1, 1.6, 500, q_2, 100.0, 1.5)
    # packet_d.prime_packet_detect()
    pass


import numpy
from PIL import Image

def get_image(image_path):
    """Get a numpy array of an image so that one can access values[x][y]."""
    image = Image.open(image_path, "r")
    width, height = image.size
    pixel_values = list(image.getdata())
    if image.mode == "RGB":
        channels = 3
    elif image.mode == "L":
        channels = 1
    else:
        print("Unknown mode: %s" % image.mode)
        return None
    pixel_values = numpy.array(pixel_values).reshape((height, width, channels))
    return pixel_values

image = get_image("spectrum.png")

for y in range(image.shape[0]):
    print('{', end='')
    for x in range(image.shape[1]):
        if (x == image.shape[1] - 1):
            print('{0:#08x}'.format((image[y][x][0] << 16) + (image[y][x][1] << 8) + image[y][x][2]), end = '')
        else:
            print('{0:#08x},'.format((image[y][x][0] << 16) + (image[y][x][1] << 8) + image[y][x][2]), end = ' ')
    print('},')


#!/usr/bin/env python3
from flask import Flask, Response, abort, request
from flask_cors import CORS

from colorize import colorize_image

api = Flask(__name__)
CORS(api)

@api.route('/colorize', methods=['POST'])
def post_image(models={
    'v1': 'colorize_v1',
    'v2': 'colorize_v2'
}):
    version = request.args.get('version')
    if version not in models:
        abort(404, "This model doesn't exist")
    if 'image' not in request.files:
        abort(400, "Please send a file with key=image")
    file = request.files['image']
    return Response(colorize_image(file, models[version]), mimetype="image/jpeg")

if __name__ == '__main__':
    api.run(host="0.0.0.0")


import hug
import datetime, asyncio
from hug import types
from lib.tools import tools
from applib.verify_lib import VerifyLib

@hug.get("/ping")
@hug.post("/ping")
async def ping(request):
    """
Description end-point

---
tags:
  - user
summary: 健康检查
description: 健康检查
produces:
  - application/json
responses:
    "201":
        description: 检查结果

    """

    return "pong"

async def hello():
    await asyncio.sleep(0.1)
    return "haha"

@hug.get("/hello")
async def hello_world(callback: types.text = ''):
    """
summary: Hello Swagger
description: |
    Swagger 是一个规范和完整的框架，用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
    总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法，参数和模型紧密集成到服务器端的代码，允许API来始终保持同步。
    Swagger 让部署管理和使用功能强大的API从未如此简单。  按照 id 逆序排列
parameters:
  - name: start_latitude
    in: query;
    description: 几个参数
    required: true
    type: number
    format: double
  - name: start_longitude
    in: query
    description: 又一个参数
    required: true
    type: number
    format: double
  - name: end_latitude
    in: query
    description: 几个参数
    required: true
    type: number
    format: double
  - name: end_longitude
    in: query
    description: 又一个
    required: true
    type: number
    format: double
tags:
    - Estimates

responses:
    200:
        description: An array of price estimates by product
        schema:
            $ref: "#/definitions/StoreList"

============================== outputs

definitions:
  Store:
    type: object
    properties:
      id:
        type: "integer"
        description: 就是一个ID啦
      name:
        type: "string"
        description: 我也不知道是超市
      country_code:
        type: "string"
        description: 国家
      city:
        type: "string"
        description: 城市
  StoreList:
    type: array
    items:
      $ref: "#/definitions/Store"
    """
    res = await hello()
    return tools.response(  {
                            "id": 0 ,
                            "city": "string" ,
                            "name": "string" ,
                            "country_code": "string" ,
                            }, code=0, message="发送成功", callback=callback)

@hug.get("/test")
async def test(callback: types.text = ''):
    '''
    '''
    return tools.response('test', code=0, message="发送成功", callback=callback)

@hug.get("/async_test")
async def async_test():
    return 'test'

@hug.get("/async_cache_test")
async def async_cache_test():
    res = await VerifyLib.async_cache_test()
    return res


from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from . models import Product
from django.utils import timezone
from django.contrib.auth.models import User

#==========================================Main HomePage=============================================
def product_home(request):
	all_products = Product.objects
	return render(request, 'products/product_home.html', {'all_products' : all_products})

#======================================Add New Product===================================================
@login_required(login_url="/accounts/login")
def add_product(request):
	if(request.method == 'POST'):
		#user filled the detail we have to store in DB.
		if(request.POST['title'] and request.POST['body'] and request.POST['url'] and request.FILES['icon'] and request.FILES['image']):
			#checking whether all fields are filled by user.

			#get  all fields.
			title = request.POST['title']
			body = request.POST['body']
			url = request.POST['url']
			icon = request.FILES['icon']
			image = request.FILES['image']

			if not (url.startswith('http://') or url.startswith('https://')):
				url = "http://"+url

			#Create object of Product model and start adding the details to that object.
			product = Product()
			product.title = title
			product.body = body
			product.url = url
			product.icon = icon
			product.image = image
			product.publish_date = timezone.datetime.now()
			product.hunter = request.user
			product.save()
			return redirect('/products/' + str(product.id))

		else:
			#All fields are not filled.
			return render(request, 'products/add_product.html', {'error' : 'Fill up complete form'})

	else:
		#user want to fill the form
		return render(request, 'products/add_product.html')

#===============================Detail of specific Product================================================
def product_details(request, prod_id):
	prod_details = get_object_or_404(Product, pk = prod_id)
	return render(request, 'products/product_details.html', {'details' : prod_details})

#===============================Increase Votes==================================================
@login_required(login_url="/accounts/login")
def upvote(request, prod_id):
	product = get_object_or_404(Product, pk = prod_id)
	product.votes += 1
	product.save()
	return redirect('/products/' + str(product.id))

#===============================Products uploaded by specific user===========================
@login_required(login_url="/accounts/login")
def my_products(request):
	user_name = User.objects.get(username=request.user.username)
	products = Product.objects.filter(hunter = user_name)
	return render(request, 'products/my_products.html', {'products' : products})


import threading
import socket

# Define socket host and port
SERVER_HOST = '127.0.0.1' # Localhost
SERVER_PORT = 9000

class SocketClient:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.nick = input("Choose your nickname: ")
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.run_client = True
        # Connect to server
        self.s.connect((SERVER_HOST, SERVER_PORT))

    def receive_message(self):
        while True and self.run_client:
            try:
                # Receive message from server
                message = self.s.recv(1024).decode('ascii')
                if message == 'ACK!':
                    continue
                if message == 'NICK':
                    self.s.send(self.nick.encode('ascii'))
                if message == 'KICK!':
                    print("You were kicked from the server!")
                    self.run_client = False
                    break
                else:
                    print(f'{message}')
            except Exception as e:
                # Close connection when error
                print(e)
                print("An error occured! receive_message")
                self.s.close()
                break

    def send_message(self):
        while True and self.run_client:
            try:
                # Send message to server
                message = f'{self.nick}: {input("Type your message:")}'
                self.s.send(message.encode('ascii'))
            except Exception as e:
                # Close connection when error
                print(e)
                print("An error occured! send_message")
                self.s.close()
                break

    def run(self):
        # Create threads for sending and receiving messages
        receive_thread = threading.Thread(target=self.receive_message)
        receive_thread.start()
        send_thread = threading.Thread(target=self.send_message)
        send_thread.start()

        # Wait until threads are finished
        receive_thread.join()
        send_thread.join()

        # Close socket
        self.s.close()

if __name__ == "__main__":
    client = SocketClient(SERVER_HOST, SERVER_PORT)
    client.run()


import json
import jsonlines
DATA_FILE_PATH = "/home/user/Downloads/exams.json"
with jsonlines.open(DATA_FILE_PATH) as data:
    total = 497
    key_dict = {}
    for line in data:
        for i, v in line.items():
            if v and isinstance(v, list):
                for value_v in v:
                    if isinstance(value_v, dict):
                        for k_value_v, v_value_v in value_v.items():
                            if v_value_v and not isinstance(v_value_v, list) and not isinstance(v_value_v, dict):
                                key_dict[k_value_v] = key_dict.get(k_value_v, 0) + 1

    key_dict_per = {}
    for k, v in key_dict.items():
    	key_dict_per[k] = (v/total)*100
    print(key_dict, key_dict_per)


def isSum(target_number, numbers):
    sums = [
        number1 + number2 for i, number1 in enumerate(
            numbers) for number2 in numbers[(i+1):]]
    return target_number in sums

def findSum(target_number, lines):
    numbers = [int(line) for line in lines]
    for set_size in range(2, len(numbers)):
        for first_number in range(len(numbers)):
            if first_number + set_size <= len(numbers):
                if sum(numbers[
                    first_number:first_number+set_size]) == target_number:
                    return max(numbers[
                        first_number:first_number+set_size]) + min(
                            numbers[first_number:first_number+set_size])

    return 'No solution found!'

with open('input_day_9.txt', 'r') as f:
    lines = f.read().split('\n')[:-1]

for i, line in enumerate(lines):
    if i > 24:
        numbers = [int(lines[k]) for k in range(i-25, i)]
        target_number = int(line)
        if not isSum(target_number, numbers):
            solution1 = line
            solution2 = findSum(target_number, lines)
            break

print('A1: ', solution1)
print('A2: ', solution2)


from transformers import RobertaTokenizerFast
from transformers import BertTokenizerFast
from transformers import AutoModelForSequenceClassification

import copy
import evaluate
import numpy as np
import os
import pandas as pd
import torch
from tqdm import tqdm

BINARY_PRETRAINED = True
#BINARY = False

test_path = '../../FFChallenge_v2/test.csv'
#TEST = "SMALL"
TEST = "MEDIUM"
#TEST = "ALL"
if TEST == "SMALL":
  train_path = '../small_train.csv'
  background_path = '../small_background_x2000.csv'
else:
  train_path = '../train.csv'
  if BINARY_PRETRAINED:
    background_path = '../binary/background_x2000.csv'
  else:
    background_path = '../background_x2000.csv'

if BINARY_PRETRAINED is False:
  tokenizer = RobertaTokenizerFast(tokenizer_file="../byte-level-BPE.tokenizer.json", max_len=max_seq_length)
else:
  tokenizer = BertTokenizerFast.from_pretrained('../binary/tokenizer')

CHALLENGE_ID = 'challengeID'
tasks = ['gpa', 'grit', 'materialHardship', 'eviction', 'layoff', 'jobTraining']

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
BATCH_SIZE = 48

#exit(0)
def load_background(path):
  if TEST == "MEDIUM":
    df = pd.read_csv(path, skiprows=lambda x: x%100!=0)
    print("RAZOR")
    print("medium loaded, columns=", df.columns)
  else:
    df = pd.read_csv(path)
  df.rename(columns={'family_id': CHALLENGE_ID}, inplace=True)
  df[CHALLENGE_ID] = df[CHALLENGE_ID] + 1
  return df

def tokenize(texts, tokenizer):
  return tokenizer(
        texts,
        padding='max_length',
        truncation=True,
        max_length=max_seq_length,
        # We use this option because DataCollatorForLanguageModeling (see below) is more efficient when it
        # receives the `special_tokens_mask`.
    )

def tokenize_background(df, tokenizer):
  encoded_text = tokenize(df['text'].tolist(), tokenizer)
  df['input_ids'] = encoded_text['input_ids']
  df['attention_mask'] = encoded_text['attention_mask']
  return df

def load_model(label):
  path = "../binary/" + label + "/final_models"
  model = AutoModelForSequenceClassification.from_pretrained(path)
  model.eval()
  return model

def run_data(model, challenge_id, background_encoded_df, k_samples):
  df = copy.deepcopy(background_encoded_df[background_encoded_df[CHALLENGE_ID] == challenge_id])
  df = df.sample(n=k_samples)
  input_ids = torch.tensor(df['input_ids'].tolist()).to(DEVICE)
  outputs = model(input_ids)
  outputs = outputs['logits'].detach().cpu().numpy()
  ret = np.mean(outputs)
  return ret

def eval_results(predictions, golds, train_y=None, train_mean = None):
  if train_mean is None:
    train_y = train_y[~np.isnan(golds)]
    train_mean = np.mean(train_y)

  predictions = predictions[~np.isnan(golds)]
  golds = golds[~np.isnan(golds)]
  #print(len(golds))
  #print("train_mean", train_mean)
  a = np.sum(np.square(predictions - golds))
  b = np.sum(np.square(train_mean - golds))
  #print(a, b, a/b)
  R = 1 - np.sum(np.square(predictions - golds)) / np.sum(np.square(train_mean - golds))
  return R

k_df = []
task_df = []
R_df = []
train_means = {
  'gpa':2.8667381974248900,
  'grit':3.4275387870239800,
  'eviction':0.0596298834818368,
  'layoff':0.2090837901331250,
  'materialHardship':0.1037447816063310,
  'jobTraining':0.2347707049965780
}
def eval_task(main_test_df, background_encoded_df, target_label, k_samples=10):
  model = load_model(target_label).to(DEVICE)
  predictions = []
  golds = []
  for i in range(len(main_test_df)):
    current_id = main_test_df.iloc[i][CHALLENGE_ID]
    current_prediction = run_data(model, current_id, background_encoded_df, k_samples)
    predictions.append(current_prediction)

  final_df = copy.deepcopy(main_test_df)
  final_df['predictions'] = predictions
  results = eval_results(final_df['predictions'].to_numpy(),
                      final_df[task].to_numpy(),
                      train_mean = train_means[task])
  k_df.append(k_samples)
  task_df.append(target_label)
  R_df.append(results)
  print(task, results)

max_seq_length = 512

background_df = load_background(background_path)
background_encoded_df = tokenize_background(background_df, tokenizer)
del background_df
#k_candidates = [1, 5, 10, 20, 100]
k_candidates = [10]
for k_samples in tqdm(k_candidates):
  for task in tasks:
    print("Working for", task)
    main_test_df = pd.read_csv(test_path)
    main_test_df = main_test_df[[CHALLENGE_ID, task]]
    main_test_df = main_test_df[main_test_df[task].notna()]
    eval_task(main_test_df, background_encoded_df, task, k_samples)

final_df = pd.DataFrame({"k":k_df, "task":task_df, "R":R_df})
final_df.to_csv('diff_k_normal_results.csv', index=False)


from __future__ import print_function as _
from __future__ import division as _
from __future__ import absolute_import as _

from coremltools.converters.mil.mil.passes.pass_registry import register_pass
from coremltools.converters.mil.mil import Builder as mb
import numpy as np

def match_pattern(op):
    if op.outputs[0] in op.enclosing_block.outputs:
        return None

    if op.op_type == "mul":
        # find add
        child_ops = op.outputs[0].child_ops
        if len(child_ops) == 1:
            add_op_candidate = list(child_ops)[0]
            if add_op_candidate.op_type == "add":
                return add_op_candidate
    return None

def _find_const_input_val(op):
    if op.x.val is not None:
        return op.x.val
    if op.y.val is not None:
        return op.y.val
    return None

def _check_shape(arr):
    """
    return True if shape is of form
    (1,C,1,1) or (C,1,1)
    """
    rank = len(arr.shape)
    if not (rank == 3 or rank == 4):
        return False
    C = arr.shape[-3]
    if not (arr.shape == (1, C, 1, 1) or arr.shape == (C, 1, 1)):
        return False
    return True

def try_to_transform(mul_op, add_op, block):
    non_const_input_mul = mul_op.x if mul_op.x.val is None else mul_op.y
    if non_const_input_mul.rank != 4:
        return False

    gamma = _find_const_input_val(mul_op)
    beta = _find_const_input_val(add_op)
    if gamma is None or beta is None:
        return False

    if not (isinstance(gamma, np.ndarray) and isinstance(beta, np.ndarray)):
        return False

    # check that gamma and beta have shape (1,C,1,1) or (C,1,1)
    # that is they are doing vector addition on the axis=-3, which is what the
    # batchnorm layer does (batchnorm layer only works on rank 4 input tensors)
    if not (_check_shape(gamma) and _check_shape(beta)):
        return False

    C = gamma.shape[-3]
    if C == 1:
        return False

    out_name = add_op.outputs[0].name
    x = mb.batch_norm(
        x=non_const_input_mul,
        mean=np.zeros((C,), np.float32),
        variance=np.ones((C,), np.float32),
        gamma=np.squeeze(gamma),
        beta=np.squeeze(beta),
        name=out_name,
        before_op=mul_op,
    )

    add_op.enclosing_block.replace_uses_of_var_after_op(
        anchor_op=add_op, old_var=add_op.outputs[0], new_var=x
    )
    # Remove all the ops at once
    block.remove_ops([mul_op, add_op])
    return True

def fuse_elementwise_to_batchnorm_block(block):
    fusion_status = False
    for op in list(block.operations):
        for b in op.blocks:
            block_changed = True
            while block_changed:
                block_changed = fuse_elementwise_to_batchnorm_block(b)
        if len(op.blocks) > 0:
            # This op can't be mul
            continue

        add_op = match_pattern(op)
        if add_op is not None:
            with block:
                fusion_status = try_to_transform(op, add_op, block)
            # has to break as the downstream iterator is affected.
            if fusion_status:
                return fusion_status
    return fusion_status

@register_pass(namespace="common")
def fuse_elementwise_to_batchnorm(prog):
    """
    Fold mul + add into a batch norm,
    if the const feeding into the mul/add is of shape (1,C,1,1) or (C,1,1)
    and input to mul is of rank 4.

    Given:
             [Const]   [Const]
                |         |
                V         V
    [...] --> [Mul] --> [Add] --> [...]

    That is,

        %2 = op1(%1)
        %3 = mul(%2, constant)
        %4 = add(%3, constant)
        %5 = op2(%4)
        ...

    Result:

    [...] --> [BatchNorm] --> [...]

    That is,
        %2 = op1(%1)
        %4 = batchnorm(%2)
        %5 = op2(%4)
        ...

    """
    for f_name, f in prog.functions.items():
        block_changed = True
        while block_changed:
            block_changed = fuse_elementwise_to_batchnorm_block(f)


import numpy as np
from itertools import count
np.set_printoptions(precision=4)

def apply(F, x):
    n = F.shape[0]
    if len(F.shape) == 2:
        res = np.zeros(shape=(n, n))
        for i in range(n):
            for j in range(n):
                res[i, j] = F[i, j](*x)
    else:
        res = np.zeros(shape=n)
        for i in range(n):
            res[i] = F[i](*x)
    return res

def solve():
    eps = 0.00001
    F = np.array([
        lambda x, y: x - 0.5 * np.sin((x - y) / 2),
        lambda x, y: y - 0.5 * np.cos((x + y) / 2)
    ], dtype=object)

    dF = np.array([
        [lambda x, y: 1 - 0.25 * np.cos((x - y) / 2), lambda x, y:     0.25 * np.cos((x - y) / 2)],
        [lambda x, y:     0.25 * np.sin((x + y) / 2), lambda x, y: 1 - 0.25 * np.sin((x + y) / 2)]
    ], dtype=object)

    x0 = (0, 0)
    print(f"x0 = {x0}\n")
    for i in count():
        A = apply(dF, x0)
        F0 = apply(F, x0)
        z = np.linalg.solve(A, F0)
        x = np.array(x0) - np.array(z)
        print(f"{A} = A{i} \t\t F(x{i}) = {F0} \t\t z{i} = {z} \t\t x{i + 1} = {x}\n")
        if np.abs(z).max() <= eps:
            return x
        x0 = x

if __name__ == '__main__':
    res = solve()


from bai14_thua_ke_giao_trinh_1 import Person

# Lớp Student (Sinh viên) mở rộng (extends) từ lớp Person.
class Student (Person):
    def __init__(self, name, age, university):
        # Gọi tới constructor của lớp cha (Person) để gán giá trị vào thuộc tính 'name' của lớp cha.
        super().__init__(name)
        self.age = age
        self.university = university

# Ghi đè (override) phương thức cùng tên của lớp cha.
    def show_info(self):
        print ("Name: " + self.name)
        print (" Age: " + str(self.age))
        print (" University: " + self.university)

# Khởi tạo đối tượng Student
student = Student("Nguyen Van A", 18, "DHQG Ha Noi")

# Gọi tới hàm show_info của lớp Student
student.show_info()


from functools import partial
import jax.numpy as jnp
from jax import vmap

from jaxopt import LBFGS

import chex
import typing_extensions
from typing import Any, Callable, NamedTuple, Optional, Union

from jsl.experimental.seql.agents.base import Agent
from jsl.experimental.seql.utils import posterior_noise

Params = Any
AutoOrBoolean = Union[str, bool]

class ModelFn(typing_extensions.Protocol):
    def __call__(self,
                 params: chex.Array,
                 inputs: chex.Array):
        ...

class ObjectiveFn(typing_extensions.Protocol):
    def __call__(self,
                 params: chex.Array,
                 inputs: chex.Array,
                 outputs: chex.Array,
                 model_fn: ModelFn):
        ...

class BeliefState(NamedTuple):
    params: Params

def lbfgs_agent(objective_fn: ObjectiveFn,
                model_fn: ModelFn = lambda mu, x: x @ mu,
                obs_noise: float = 1.,
                has_aux: bool = False,
                maxiter: int = 500,
                tol: float = 1e-3,
                condition: str = "strong-wolfe",
                maxls: int = 15,
                decrease_factor: float = 0.8,
                increase_factor: float = 1.5,
                buffer_size: int = 10,
                use_gamma: bool = True,
                implicit_diff: bool = True,
                implicit_diff_solve: Optional[Callable] = None,
                jit: AutoOrBoolean = "auto",
                unroll: AutoOrBoolean = "auto",
                verbose: bool = False):
    '''
    https://github.com/google/jaxopt/blob/53b539e6c5cee4c52262ce17d4601839422ffe87/jaxopt/_src/lbfgs.py#L148
    Attributes:
        fun: a smooth function of the form ``fun(x, *args, **kwargs)``.
        has_aux: whether function fun outputs one (False) or more values (True).
        When True it will be assumed by default that fun(...)[0] is the objective.
        maxiter: maximum number of proximal gradient descent iterations.
        tol: tolerance of the stopping criterion.
        maxls: maximum number of iterations to use in the line search.
        decrease_factor: factor by which to decrease the stepsize during line search
        (default: 0.8).
        increase_factor: factor by which to increase the stepsize during line search
        (default: 1.5).
        history_size: size of the memory to use.
        use_gamma: whether to initialize the inverse Hessian approximation with
        gamma * I, see 'Numerical Optimization', equation (7.20).
        implicit_diff: whether to enable implicit diff or autodiff of unrolled
        iterations.
        implicit_diff_solve: the linear system solver to use.
        jit: whether to JIT-compile the optimization loop (default: "auto").
        unroll: whether to unroll the optimization loop (default: "auto").
        verbose: whether to print error on every iteration or not.
        Warning: verbose=True will automatically disable jit.
    Reference:
        Jorge Nocedal and Stephen Wright.
        Numerical Optimization, second edition.
        Algorithm 7.5 (page 179).
    '''

    partial_objective_fn = partial(objective_fn,
                                   model_fn=model_fn)

    lbfgs = LBFGS(partial_objective_fn,
                  has_aux,
                  maxiter,
                  tol,
                  condition,
                  maxls,
                  decrease_factor,
                  increase_factor,
                  buffer_size,
                  use_gamma,
                  implicit_diff,
                  implicit_diff_solve,
                  jit,
                  unroll,
                  verbose)

    def init_state(params: Params):
        return BeliefState(params)

    def update(belief: BeliefState,
               x: chex.Array,
               y: chex.Array):
        params, info = lbfgs.run(belief.params,
                                 inputs=x,
                                 outputs=y)
        return BeliefState(params), info

    def predict(belief: BeliefState,
                x: chex.Array):
        d, *_ = x.shape
        noise = obs_noise * jnp.eye(d)
        return model_fn(belief.params, x), noise

    return Agent(init_state, update, predict)


import numpy as np
import os
import isceobj
import datetime
import logging

logger = logging.getLogger('isce.topsinsar.topo')

def runTopo(self):

    hasGPU= self.useGPU and self._insar.hasGPU()
    if hasGPU:
        runTopoGPU(self)
    else:
        runTopoCPU(self)

def runTopoCPU(self):
    from zerodop.topozero import createTopozero
    from isceobj.Planet.Planet import Planet

    swathList = self._insar.getValidSwathList(self.swaths)

    ####Catalog for logging
    catalog = isceobj.Catalog.createCatalog(self._insar.procDoc.name)

    ####Load in DEM
    demfilename = self.verifyDEM()
    catalog.addItem('Dem Used', demfilename, 'topo')

    boxes = []
    for swath in swathList:
        #####Load the reference product
        reference = self._insar.loadProduct( os.path.join(self._insar.referenceSlcProduct,  'IW{0}.xml'.format(swath)))

        numCommon  = self._insar.numberOfCommonBursts[swath-1]
        startIndex = self._insar.commonBurstStartReferenceIndex[swath-1]

        if numCommon > 0:
            catalog.addItem('Number of common bursts IW-{0}'.format(swath), self._insar.numberOfCommonBursts[swath-1], 'topo')

            ###Check if geometry directory already exists.
            dirname = os.path.join(self._insar.geometryDirname, 'IW{0}'.format(swath))
            os.makedirs(dirname, exist_ok=True)

            ###For each burst
            for index in range(numCommon):
                ind = index + startIndex
                burst = reference.bursts[ind]

                latname = os.path.join(dirname, 'lat_%02d.rdr'%(ind+1))
                lonname = os.path.join(dirname, 'lon_%02d.rdr'%(ind+1))
                hgtname = os.path.join(dirname, 'hgt_%02d.rdr'%(ind+1))
                losname = os.path.join(dirname, 'los_%02d.rdr'%(ind+1))

                demImage = isceobj.createDemImage()
                demImage.load(demfilename + '.xml')

                #####Run Topo
                planet = Planet(pname='Earth')
                topo = createTopozero()
                topo.slantRangePixelSpacing = burst.rangePixelSize
                topo.prf = 1.0/burst.azimuthTimeInterval
                topo.radarWavelength = burst.radarWavelength
                topo.orbit = burst.orbit
                topo.width = burst.numberOfSamples
                topo.length = burst.numberOfLines
                topo.wireInputPort(name='dem', object=demImage)
                topo.wireInputPort(name='planet', object=planet)
                topo.numberRangeLooks = 1
                topo.numberAzimuthLooks = 1
                topo.lookSide = -1
                topo.sensingStart = burst.sensingStart
                topo.rangeFirstSample = burst.startingRange
                topo.demInterpolationMethod='BIQUINTIC'
                topo.latFilename = latname
                topo.lonFilename = lonname
                topo.heightFilename = hgtname
                topo.losFilename = losname
                topo.topo()

                bbox = [topo.minimumLatitude, topo.maximumLatitude, topo.minimumLongitude, topo.maximumLongitude]
                boxes.append(bbox)

                catalog.addItem('Number of lines for burst {0} - IW-{1}'.format(index,swath), burst.numberOfLines, 'topo')
                catalog.addItem('Number of pixels for bursts {0} - IW-{1}'.format(index,swath), burst.numberOfSamples, 'topo')
                catalog.addItem('Bounding box for burst {0} - IW-{1}'.format(index,swath), bbox, 'topo')

        else:
            print('Skipping Processing for Swath {0}'.format(swath))

        topo = None

    boxes = np.array(boxes)
    bbox = [np.min(boxes[:,0]), np.max(boxes[:,1]), np.min(boxes[:,2]), np.max(boxes[:,3])]
    catalog.addItem('Overall bounding box', bbox, 'topo')

    catalog.printToLog(logger, "runTopo")
    self._insar.procDoc.addAllFromCatalog(catalog)

    return

def runTopoGPU(self):
    '''
    Try with GPU module.
    '''

    from isceobj.Planet.Planet import Planet
    from zerodop.GPUtopozero.GPUtopozero import PyTopozero
    from isceobj import Constants as CN
    from isceobj.Util.Poly2D import Poly2D
    from iscesys import DateTimeUtil as DTU

    swathList = self._insar.getValidSwathList(self.swaths)

    ####Catalog for logging
    catalog = isceobj.Catalog.createCatalog(self._insar.procDoc.name)

    ####Load in DEM
    demfilename = self.verifyDEM()
    catalog.addItem('Dem Used', demfilename, 'topo')

    frames = []
    swaths = []
    swathStarts = []

    for swath in swathList:
        #####Load the reference product
        reference = self._insar.loadProduct( os.path.join(self._insar.referenceSlcProduct,  'IW{0}.xml'.format(swath)))

        numCommon  = self._insar.numberOfCommonBursts[swath-1]
        startIndex = self._insar.commonBurstStartReferenceIndex[swath-1]

        if numCommon > 0:
            catalog.addItem('Number of common bursts IW-{0}'.format(swath), self._insar.numberOfCommonBursts[swath-1], 'topo')

            reference.bursts = reference.bursts[startIndex:startIndex+numCommon]
            reference.numberOfBursts = numCommon

            frames.append(reference)
            swaths.append(swath)
            swathStarts.append(startIndex)

    if len(frames) == 0:
        raise Exception('There is no common region between the two dates to process')

    topSwath = min(frames, key=lambda x: x.sensingStart)
    leftSwath = min(frames, key=lambda x: x.startingRange)
    bottomSwath = max(frames, key=lambda x: x.sensingStop)
    rightSwath = max(frames, key=lambda x: x.farRange)

    r0 = leftSwath.startingRange
    rmax = rightSwath.farRange
    dr = frames[0].bursts[0].rangePixelSize
    t0 = topSwath.sensingStart
    tmax = bottomSwath.sensingStop
    dt = frames[0].bursts[0].azimuthTimeInterval
    wvl = frames[0].bursts[0].radarWavelength
    width = int(np.round((rmax-r0)/dr) + 1)
    lgth = int(np.round((tmax-t0).total_seconds()/dt) + 1)

    polyDoppler = Poly2D(name='topsApp_dopplerPoly')
    polyDoppler.setWidth(width)
    polyDoppler.setLength(lgth)
    polyDoppler.setNormRange(1.0)
    polyDoppler.setNormAzimuth(1.0)
    polyDoppler.setMeanRange(0.0)
    polyDoppler.setMeanAzimuth(0.0)
    polyDoppler.initPoly(rangeOrder=0,azimuthOrder=0, coeffs=[[0.]])
    polyDoppler.createPoly2D()

    slantRangeImage = Poly2D()
    slantRangeImage.setWidth(width)
    slantRangeImage.setLength(lgth)
    slantRangeImage.setNormRange(1.0)
    slantRangeImage.setNormAzimuth(1.0)
    slantRangeImage.setMeanRange(0.)
    slantRangeImage.setMeanAzimuth(0.)
    slantRangeImage.initPoly(rangeOrder=1,azimuthOrder=0,coeffs=[[r0,dr]])
    slantRangeImage.createPoly2D()

    dirname = self._insar.geometryDirname
    os.makedirs(dirname, exist_ok=True)

    latImage = isceobj.createImage()
    latImage.initImage(os.path.join(dirname, 'lat.rdr'), 'write', width, 'DOUBLE')
    latImage.createImage()

    lonImage = isceobj.createImage()
    lonImage.initImage(os.path.join(dirname, 'lon.rdr'), 'write', width, 'DOUBLE')
    lonImage.createImage()

    losImage = isceobj.createImage()
    losImage.initImage(os.path.join(dirname, 'los.rdr'), 'write', width, 'FLOAT', bands=2, scheme='BIL')
    losImage.setCaster('write', 'DOUBLE')
    losImage.createImage()

    heightImage = isceobj.createImage()
    heightImage.initImage(os.path.join(dirname, 'hgt.rdr'),'write',width,'DOUBLE')
    heightImage.createImage()

    demImage = isceobj.createDemImage()
    demImage.load(demfilename + '.xml')
    demImage.setCaster('read', 'FLOAT')
    demImage.createImage()

    orb = self._insar.getMergedOrbit(frames)
    pegHdg = np.radians( orb.getENUHeading(t0))

    elp = Planet(pname='Earth').ellipsoid

    topo = PyTopozero()
    topo.set_firstlat(demImage.getFirstLatitude())
    topo.set_firstlon(demImage.getFirstLongitude())
    topo.set_deltalat(demImage.getDeltaLatitude())
    topo.set_deltalon(demImage.getDeltaLongitude())
    topo.set_major(elp.a)
    topo.set_eccentricitySquared(elp.e2)
    topo.set_rSpace(dr)
    topo.set_r0(r0)
    topo.set_pegHdg(pegHdg)
    topo.set_prf(1.0/dt)
    topo.set_t0(DTU.seconds_since_midnight(t0))
    topo.set_wvl(wvl)
    topo.set_thresh(.05)
    topo.set_demAccessor(demImage.getImagePointer())
    topo.set_dopAccessor(polyDoppler.getPointer())
    topo.set_slrngAccessor(slantRangeImage.getPointer())
    topo.set_latAccessor(latImage.getImagePointer())
    topo.set_lonAccessor(lonImage.getImagePointer())
    topo.set_losAccessor(losImage.getImagePointer())
    topo.set_heightAccessor(heightImage.getImagePointer())
    topo.set_incAccessor(0)
    topo.set_maskAccessor(0)
    topo.set_numIter(25)
    topo.set_idemWidth(demImage.getWidth())
    topo.set_idemLength(demImage.getLength())
    topo.set_ilrl(-1)
    topo.set_extraIter(10)
    topo.set_length(lgth)
    topo.set_width(width)
    topo.set_nRngLooks(1)
    topo.set_nAzLooks(1)
    topo.set_demMethod(5) # BIQUINTIC METHOD
    topo.set_orbitMethod(0) # HERMITE

    # Need to simplify orbit stuff later
    nvecs = len(orb._stateVectors)
    topo.set_orbitNvecs(nvecs)
    topo.set_orbitBasis(1) # Is this ever different?
    topo.createOrbit() # Initializes the empty orbit to the right allocated size
    count = 0
    for sv in orb._stateVectors:
        td = DTU.seconds_since_midnight(sv.getTime())
        pos = sv.getPosition()
        vel = sv.getVelocity()
        topo.set_orbitVector(count,td,pos[0],pos[1],pos[2],vel[0],vel[1],vel[2])
        count += 1

    topo.runTopo()

    latImage.addDescription('Pixel-by-pixel latitude in degrees.')
    latImage.finalizeImage()
    latImage.renderHdr()

    lonImage.addDescription('Pixel-by-pixel longitude in degrees.')
    lonImage.finalizeImage()
    lonImage.renderHdr()

    heightImage.addDescription('Pixel-by-pixel height in meters.')
    heightImage.finalizeImage()
    heightImage.renderHdr()

    descr = '''Two channel Line-Of-Sight geometry image (all angles in degrees). Represents vector drawn from target to platform.
            Channel 1: Incidence angle measured from vertical at target (always +ve).
            Channel 2: Azimuth angle measured from North in Anti-clockwise direction.'''
    losImage.setImageType('bil')
    losImage.addDescription(descr)
    losImage.finalizeImage()
    losImage.renderHdr()

    demImage.finalizeImage()

    if slantRangeImage:
        try:
            slantRangeImage.finalizeImage()
        except:
            pass

    ####Start creating VRTs to point to global topo output
    for swath, frame, istart in zip(swaths, frames, swathStarts):
        outname = os.path.join(dirname, 'IW{0}'.format(swath))

        os.makedirs(outname, exist_ok=True)

        for ind, burst in enumerate(frame.bursts):
            top = int(np.rint((burst.sensingStart - t0).total_seconds()/dt))
            bottom = top + burst.numberOfLines
            left = int(np.rint((burst.startingRange - r0)/dr))
            right = left + burst.numberOfSamples

            buildVRT( os.path.join(dirname, 'lat.rdr'),
                      os.path.join(outname, 'lat_%02d.rdr'%(ind+istart+1)),
                      [width, lgth],
                      [top,bottom, left, right],
                      bands=1,
                      dtype='DOUBLE')

            buildVRT( os.path.join(dirname, 'lon.rdr'),
                      os.path.join(outname, 'lon_%02d.rdr'%(ind+istart+1)),
                      [width, lgth],
                      [top,bottom, left, right],
                      bands=1,
                      dtype='DOUBLE')

            buildVRT( os.path.join(dirname, 'hgt.rdr'),
                      os.path.join(outname, 'hgt_%02d.rdr'%(ind+istart+1)),
                      [width, lgth],
                      [top,bottom, left, right],
                      bands=1,
                      dtype='DOUBLE')

            buildVRT( os.path.join(dirname, 'los.rdr'),
                      os.path.join(outname, 'los_%02d.rdr'%(ind+istart+1)),
                      [width, lgth],
                      [top,bottom, left, right],
                      bands=2,
                      dtype='FLOAT')

            catalog.addItem('Subset for IW{0}-B{1}'.format(swath, ind+1+istart), 'Lines: {0}-{1} out of {2}, Pixels: {3}-{4} out of {5}'.format(top, bottom, lgth, left, right, width), 'topo')

#            print('IW{0}-B{1}: {2} - {3}/ {4}, {5} - {6} /{7}'.format(swath, ind+1+istart, top, bottom, lgth, left, right, width))

    catalog.printToLog(logger, "runTopo")
    self._insar.procDoc.addAllFromCatalog(catalog)

    return

def buildVRT(srcname, dstname, dims, bbox, bands=1, dtype='FLOAT'):
    '''
    Write a VRT to point to the parent mosaicked file.
    '''

    header='<VRTDataset rasterXSize="{width}" rasterYSize="{lgth}">'
    band = '''    <VRTRasterBand dataType="{dtype}" band="{band}">
        <NoDataValue>0.0</NoDataValue>
        <SimpleSource>
            <SourceFilename relativeToVRT="1">{relpath}</SourceFilename>
            <SourceBand>{band}</SourceBand>
            <SourceProperties RasterXSize="{gwidth}" RasterYSize="{glgth}" DataType="{dtype}"/>
            <SrcRect xOff="{left}" yOff="{top}" xSize="{width}" ySize="{lgth}"/>
            <DstRect xOff="0" yOff="0" xSize="{width}" ySize="{lgth}"/>
        </SimpleSource>
    </VRTRasterBand>
'''
    tail = "</VRTDataset>"

    width = bbox[3] - bbox[2]
    lgth = bbox[1] - bbox[0]

    odtype = dtype
    if dtype.upper() == 'FLOAT':
        dtype = 'Float32'
    elif dtype.upper() == 'DOUBLE':
        dtype = 'Float64'
    elif dtype.upper() == 'BYTE':
        dtype = 'UInt8'
    else:
        raise Exception('Unsupported type {0}'.format(dtype))

    relpath = os.path.relpath(srcname + '.vrt', os.path.dirname(dstname))
    gwidth = dims[0]
    glgth = dims[1]
    left = bbox[2]
    top = bbox[0]

    img = isceobj.createImage()
    img.bands = bands
    img.scheme = 'BIL'
    img.setWidth(width)
    img.setLength(lgth)
    img.dataType = odtype
    img.filename = dstname
    img.setAccessMode('READ')
    img.renderHdr()

    with open(dstname + '.vrt', 'w') as fid:
        fid.write( header.format(width=width, lgth=lgth) + '\n')

        for bnd in range(bands):
            fid.write( band.format(width=width, lgth=lgth,
                                   gwidth=gwidth, glgth=glgth,
                                   left=left, top=top,
                                   relpath=relpath, dtype=dtype,
                                   band=bnd+1))

        fid.write(tail + '\n')


from loader import bot
from database import DeadlinesModel, StudentsModel, HomeworksModel
from handlers.students.deadlines.utils import get_delta_days
from datetime import datetime

async def send_deadline_to_group(deadline, delta_days):
    students = await StudentsModel.get_students_by_filters(group=deadline.group)
    student_ids = [student.vk_id for student in students]
    text = f"Напоминаю! Скоро дедлайн: {deadline.text}\nОсталось дней: {delta_days}"
    await bot.send_message(
        chat_id=student_ids,
        text=text
    )

async def send_deadlines():
    deadlines = await DeadlinesModel.get_deadlines()
    for deadline in deadlines:
        delta_days = get_delta_days(deadline.date, datetime.now().date())
        if delta_days in (7, 3, 1):
            await send_deadline_to_group(deadline, delta_days)

async def delete_homeworks():
    homeworks = await HomeworksModel.get_homeworks()
    for homework in homeworks:
        if homework.date < datetime.now().date():
            homework.delete()

async def delete_deadlines():
    deadlines = await DeadlinesModel.get_deadlines()
    for deadline in deadlines:
        if deadline.date < datetime.now().date():
            deadline.delete()