#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""

Functions for the inference.

"""
import time

import torch
from torch.optim import SGD
from torch import nn
from torch.nn.functional import softmax

from . import ParagraphVectorVariant

class ParagraphVectorInference(nn.Module):
    def __init__(
        self,
        R_array,
        P_array,
        variant=ParagraphVectorVariant.PVDBOW,
        mode="true",
        context_size=5,
        alpha=None,
    ):
        super().__init__()
        self.R_array = R_array.detach().cpu()
        self.P_array = P_array.detach().cpu()
        self.variant = variant
        self.mode = mode
        self.context_size = context_size
        if alpha:
            self.alpha = alpha
        else:
            self.alpha = 0.0001
        self.q = nn.Parameter(torch.zeros(self.R_array.shape[1]), requires_grad=True)

    def forward(self, orig, new=None, lbda=None):
        if self.mode == "true":
            obj = global_psi(
                self.q,
                orig,
                self.R_array,
                model=self.variant,
                P_matrix=self.P_array,
                context_size=self.context_size,
            ).sum()
        elif self.mode == "linear":
            obj_orig = global_psi(
                self.q,
                orig,
                self.R_array,
                model=self.variant,
                P_matrix=self.P_array,
                context_size=self.context_size,
            ).sum()
            obj_new = global_psi(
                self.q,
                new,
                self.R_array,
                model=self.variant,
                P_matrix=self.P_array,
                context_size=self.context_size,
            ).sum()
            obj = lbda * obj_new + (1 - lbda) * obj_orig
        return 1.0 / len(orig) * obj + 0.5 * self.alpha * torch.norm(self.q) ** 2

    def infer(
        self, tokenized_doc, n_steps, gamma, track_objective=False, verbose=False
    ):
        optimizer = SGD(params=self.parameters(), lr=gamma)

        infer_start = time.time()
        loss_values = []
        for i_epoch in range(n_steps):
            x = self(tokenized_doc)
            loss_values.append(x.item())
            self.zero_grad()
            x.backward()
            optimizer.step()
        infer_end = time.time()

        if verbose:
            print("auto ({:.4f}s)".format(infer_end - infer_start))

        return self.q.detach(), None, loss_values

def global_neighborhood(T, context_size=5):
    """
    All neighborhood in one array.

    PARAMETERS:
        T (int) length of the example
        context_size (int) window size, defaults to 5

    OUTPUT:
        (T-2*context_size,2*context_size) array with all neighborhood indices

    """
    aux_0 = torch.arange(context_size)
    aux_1 = torch.ones((T - 2 * context_size, 2 * context_size), dtype=torch.int)
    start = torch.cat((aux_0, context_size + 1 + aux_0))
    incr = torch.cumsum(aux_1, dim=0) - aux_1
    return start + incr

def global_context(example, context_size=5):
    """
    All contexts in one array.

    PARAMETERS:
        example (int list) encoded text
        context_size (int) window size, defaults to 5

    OUTPUT:
        (T-2*context_size,2*context_size) array with all context word indices
    """
    T = len(example)
    gn = global_neighborhood(T, context_size=context_size)
    return example[gn]

def global_context_vectors(example, P_matrix, model, context_size=5):
    """
    All context vectors for PVDM-mean.

    OUTPUT:
        (dim,T-2*context_size) array with all context vectors
    """
    gc = global_context(example, context_size=context_size)
    if model == ParagraphVectorVariant.PVDMconcat:
        T = len(example)
        D = int(P_matrix.shape[1] / (2 * context_size))
        indices = gc + D * torch.tile(
            torch.arange(2 * context_size), (T - 2 * context_size, 1)
        )
        return torch.sum(P_matrix[:, indices], dim=2)
    elif model == ParagraphVectorVariant.PVDMmean:
        return torch.sum(P_matrix[:, gc], dim=2)
    else:
        raise NotImplementedError

def global_softmax(
    q_vec,
    example,
    R_matrix,
    model=ParagraphVectorVariant.PVDBOW,
    P_matrix=None,
    context_size=5,
):
    """
    All the softmax values for the doc.

    OUTPUT:
        (D,T-2*context_size) array with all softmax values
    """
    T = len(example)
    if model == ParagraphVectorVariant.PVDBOW:
        aux_h = torch.tile(torch.matmul(R_matrix, q_vec), (T - 2 * context_size, 1)).T
        sm = softmax(aux_h, dim=0)
    elif (
        model == ParagraphVectorVariant.PVDMmean
        or model == ParagraphVectorVariant.PVDMconcat
    ):
        gcv = global_context_vectors(
            example, P_matrix, model=model, context_size=context_size
        )
        aux_h = (
            torch.matmul(R_matrix, gcv)
            + torch.tile(torch.matmul(R_matrix, q_vec), (T - 2 * context_size, 1)).T
        )
        sm = softmax(aux_h, dim=0)
    else:
        raise NotImplementedError
    return sm

def global_psi(
    q_vec,
    example,
    R_matrix,
    model=ParagraphVectorVariant.PVDBOW,
    P_matrix=None,
    context_size=5,
):
    """
    All values of \psi
    """
    T = len(example)
    aux_s = global_softmax(
        q_vec,
        example,
        R_matrix,
        model=model,
        P_matrix=P_matrix,
        context_size=context_size,
    )
    psi_wt = -torch.log(
        aux_s[
            example[context_size : T - context_size], torch.arange(T - 2 * context_size)
        ]
    )
    return psi_wt


"""mahjong URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.8/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Add an import:  from blog import urls as blog_urls
    2. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^create', views.create_view),
    url(r'^list_json', views.list_json_view),
    url(r'^list', views.list_view),
    url(r'^delete_game', views.delete_game_view),
    url(r'^record/(\d+)', views.record_view),
    url(r'^show/(\d+)', views.show_view),
    url(r'^.*$', views.list_view),
]


#! python
import argparse

import numpy as np
from numpy.random import default_rng
from scipy.spatial.transform import Rotation
import toml

from syssim import NodeSystem
from syssim.nodes.source import NodeConstant

from saas.dynamics import (
    NodeSCOrbitalDynamics,
    NodeSCRigidBodyRotationDynamics,
)
from saas.viz import NodeSCAttitudeScope, NodeSCTrajScope, NodeVizAttitudeError
from saas.controller import NodePointingControlSimple
from saas.actuator import NodeRWASimple
from saas.fs import (
    NodePointingNadir,
    NodePointingCom,
    NodePointingSunStuck,
    NodePointingCooling,
)
from saas.utility import NodeQuatRotation
from saas.sensor import NodeIMUSimple, NodeStellarReferenceUnitSimple
from saas.metrics import (
    NodeMetricPointingError,
    NodeMetricSoCTemperature,
    NodeMetricScience,
)
from saas.environment import (
    NodeEarthPosition,
    NodeSunPosition,
    NodeEarthOcclusion,
    NodeSunOcclusion,
)
from saas.power import NodeSimpleBattery
from saas.structure import NodeSimpleRectanglePrismSCBus
from saas.power import NodeSimpleGimbalSolarPanel
from saas.fs import NodeSimpleFlightSoftware, NodeFaultProtectionMGS
from saas.utility import get_poliastro_body, NodeRandomTimeDate

def circular_orbit_x0(r_low, r_high, mu):
    r = default_rng().uniform(r_low, r_high)
    theta = default_rng().uniform(0, np.pi)
    phi = default_rng().uniform(-np.pi, np.pi)

    r_eci = np.array(
        [
            r * np.sin(theta) * np.cos(phi),
            r * np.sin(theta) * np.sin(phi),
            r * np.cos(theta),
        ]
    )

    v = np.sqrt(mu / r)

    e_theta = np.array(
        [np.cos(theta) * np.cos(phi), np.cos(theta) * np.sin(phi), -np.sin(theta)]
    )
    e_phi = np.array([-np.sin(phi), np.cos(phi), 0])

    n_theta = default_rng().uniform(0, 1)
    n_phi = np.sqrt(1 - n_theta**2)

    v_eci = v * (n_theta * e_theta + n_phi * e_phi)

    return np.concatenate([r_eci, v_eci])

def circular_orbit_yz_plane_x0(r_low, r_high, mu):
    r = default_rng().uniform(r_low, r_high)
    theta = 0
    phi = np.radians(90)

    r_eci = np.array(
        [
            r * np.sin(theta) * np.cos(phi),
            r * np.sin(theta) * np.sin(phi),
            r * np.cos(theta),
        ]
    )

    v = np.sqrt(mu / r)

    e_theta = np.array(
        [np.cos(theta) * np.cos(phi), np.cos(theta) * np.sin(phi), -np.sin(theta)]
    )
    e_phi = np.array([-np.sin(phi), np.cos(phi), 0])

    # n_theta = 0
    # n_phi = 1

    v_eci = v * e_theta

    return np.concatenate([r_eci, v_eci])

def rigid_body_x0(w_low, w_high):
    # qx, qy, qz, qw = Rotation.from_euler("x", 0, degrees=True).as_quat()
    qx, qy, qz, qw = Rotation.random().as_quat(canonical=True)
    # qx, qy, qz, qw = Rotation.identity().as_quat(canonical=True)

    q0 = np.array([qw, qx, qy, qz])

    w = default_rng().uniform(w_low, w_high)
    theta = default_rng().uniform(0, np.pi)
    phi = default_rng().uniform(-np.pi, np.pi)
    w_sc = np.array(
        [
            w * np.sin(theta) * np.cos(phi),
            w * np.sin(theta) * np.sin(phi),
            w * np.cos(theta),
        ]
    )
    # w_sc = np.array([w, 0, 0])
    # w_sc = np.array([0, 0, 0])

    return np.concatenate([q0, w_sc])

parser = argparse.ArgumentParser(
    description="The Simulation for the Analysis of Autonomy at a System-level",
    prog="saas",
)
parser.add_argument(
    "node_config",
    type=str,
    help="Path to the node configuration parameter specification",
)
parser.add_argument(
    "-fc",
    "--fault-config",
    type=str,
    help="Path to the fault configuration specification",
    default=None,
    dest="fault_config",
)
parser.add_argument(
    "-d",
    "--sim-duration",
    type=float,
    help="Simulation duration in seconds",
    default=7200,
    dest="sim_duration",
)
parser.add_argument("--dt", help="Default simulation timestep", type=float, default=1.0)
parser.add_argument(
    "-b", "--batches", type=int, help="Number of simulation batches to run", default=1
)
parser.add_argument(
    "-n",
    "--sim-name",
    type=str,
    help="Name of this simulation for record keeping",
    default=None,
    dest="sim_name",
)
parser.add_argument(
    "-s",
    "--save-dir",
    type=str,
    help="Directory to save the ouputs of this simulation",
    default=None,
    dest="save_dir",
)

args = parser.parse_args()

config = toml.load(args.node_config)

central_body = get_poliastro_body(
    config["NodeSCOrbitalDynamics-1"]["central_body_name"]
)

x0_orbit = circular_orbit_x0(
    central_body.R_mean.value + 370e3,
    central_body.R_mean.value + 430e3,
    central_body.k.value,
)

x0_rb = rigid_body_x0(0.001, 0.005)

system = NodeSystem()
node_orbit = NodeSCOrbitalDynamics(x0_orbit, config=args.node_config)
node_rb = NodeSCRigidBodyRotationDynamics(x0_rb, config=args.node_config)
node_orbit_scope = NodeSCTrajScope(config=args.node_config)
node_orbit_scope.period = 30.0
node_att_scope = NodeSCAttitudeScope(config=args.node_config)
node_att_scope.period = 30.0
node_imu = NodeIMUSimple(config=args.node_config)
node_imu.frequency = 1.0
node_sru = NodeStellarReferenceUnitSimple(config=args.node_config)
node_sru.frequency = 1.0
node_rwa = NodeRWASimple(np.zeros((3,)), config=args.node_config, name="rwa")
node_rwa.frequency = 60.0
node_control = NodePointingControlSimple(np.zeros((3,)), config=args.node_config)
node_control.frequency = 1.0
node_nadir_pointing = NodePointingNadir(config=args.node_config)
node_com_pointing = NodePointingCom(config=args.node_config)
node_com_pointing.period = 60
node_ss_pointing = NodePointingSunStuck(config=args.node_config)
node_ss_pointing.period = 60
node_cool_pointing = NodePointingCooling(config=args.node_config)
node_cool_pointing.period = 60
node_metric_pointing = NodeMetricPointingError(config=args.node_config)
node_x_unit = NodeConstant(np.array([1, 0, 0]))
node_boresight = NodeQuatRotation()
node_sun = NodeSunPosition(config=args.node_config)
node_sun.period = 30 * 60.0
node_earth = NodeEarthPosition(config=args.node_config)
node_earth.period = 30 * 60.0
node_occlusion = NodeSunOcclusion(config=args.node_config)
node_occlusion.period = 60.0
node_earth_occlusion = NodeEarthOcclusion(config=args.node_config)
node_earth_occlusion.period = 60.0
node_batt = NodeSimpleBattery(np.array([0.50]), config=args.node_config)
node_batt_draw = NodeConstant(5.0)
node_thermal = NodeSimpleRectanglePrismSCBus(np.array([275]), config=args.node_config)
node_solar_panel = NodeSimpleGimbalSolarPanel(config=args.node_config)
node_solar_panel.frequency = 10.0
node_metric_soc_temp = NodeMetricSoCTemperature(config=args.node_config)
node_viz_att_cntrl_error = NodeVizAttitudeError(config=args.node_config)
node_fs = NodeSimpleFlightSoftware(config=args.node_config, name="fs")
node_metric_science = NodeMetricScience(config=args.node_config)
node_fault_mgs = NodeFaultProtectionMGS(config=args.node_config)
node_fault_mgs.frequency = 10
node_start_datetime = NodeRandomTimeDate(config=args.node_config)

system.add_node(node_orbit)
system.add_node(node_rb)
system.add_node(node_imu)
system.add_node(node_sru)
system.add_node(node_rwa)
system.add_node(node_control)
system.add_node(node_nadir_pointing)
system.add_node(node_com_pointing)
system.add_node(node_ss_pointing)
system.add_node(node_cool_pointing)
system.add_node(node_x_unit)
system.add_node(node_boresight)
system.add_node(node_sun)
system.add_node(node_earth)
system.add_node(node_occlusion)
system.add_node(node_earth_occlusion)
system.add_node(node_batt)
system.add_node(node_batt_draw)
system.add_node(node_thermal)
system.add_node(node_solar_panel)
system.add_node(node_fs)
system.add_node(node_fault_mgs)
system.add_node(node_start_datetime)

system.add_node(node_orbit_scope)
system.add_node(node_att_scope)
system.add_node(node_metric_pointing)
system.add_node(node_metric_soc_temp)
system.add_node(node_viz_att_cntrl_error)
system.add_node(node_metric_science)

# Other nodes
"""
x Thermal model
x Solar panel model
x Battery model + constant rate of drain
x Earth direction
x Sun direction
x Occlusion
- Output metrics + plotting faults
- Fault definitions
- Flight software
- auto FP node
"""

node_thermal["out_sc_mass"] = node_orbit["input_mass"]
node_thermal["out_sc_inertia_moment"] = node_control["input_sc_inertia_moment"]
node_thermal["out_sc_inertia_moment"] = node_rb["input_inertia_moment"]

node_orbit["output_a_ff_eci"] = node_imu["true_a"]
node_rb["output_w_sc"] = node_imu["true_w"]
node_rb["output_q_sc_to_eci"] = node_imu["q_sc2eci"]

node_rb["output_q_sc_to_eci"] = node_sru["input_q_sc2eci"]

# pointing solutions
node_orbit["output_r_eci"] = node_nadir_pointing["input_r_eci"]
node_orbit["output_v_eci"] = node_nadir_pointing["input_v_eci"]
# node_fs['output_fs_state'] = node_nadir_pointing["input_fs_state"]

node_sun["out_sun_unit_icrs"] = node_com_pointing["in_sun_n"]
node_earth["out_earth_unit_icrs"] = node_com_pointing["in_earth_n"]
# node_fs['output_fs_state'] = node_com_pointing["input_fs_state"]

node_sun["out_sun_unit_icrs"] = node_ss_pointing["in_sun_n"]
node_earth["out_earth_unit_icrs"] = node_ss_pointing["in_earth_n"]
# node_fs['output_fs_state'] = node_ss_pointing["input_fs_state"]

node_sun["out_sun_unit_icrs"] = node_cool_pointing["in_sun_n"]
# node_fs['output_fs_state'] = node_cool_pointing["input_fs_state"]

node_fs["out_q_cmd"] = node_control["input_q_cmd"]
node_sru["output_q_sc2eci_measure"] = node_control["input_q"]
node_fs["out_w_cmd"] = node_control["input_w_cmd"]

node_imu["measure_w"] = node_control["input_w"]
node_rb["output_w_sc"] = node_control["input_w"]
node_rwa["rwa_mtm"] = node_control["input_mtm_int"]

node_control["output_tau_cmd"] = node_rwa["tau_cmd"]

node_rwa["rwa_mtm"] = node_rb["input_mtm_internal_sc"]
node_rwa["rwa_tau"] = node_rb["input_tau_external_sc"]

node_x_unit["output"] = node_boresight["in_vec"]
node_sru["output_q_sc2eci_measure"] = node_boresight["in_quat"]

node_orbit["output_r_eci"] = node_earth["in_sc_pos_icrs"]
node_orbit["output_r_eci"] = node_sun["in_sc_pos_icrs"]
node_orbit["output_r_eci"] = node_occlusion["in_sc_pos_icrs"]
node_orbit["output_r_eci"] = node_earth_occlusion["in_sc_pos_icrs"]

node_start_datetime["out_datetime"] = node_earth["in_start_datetime"]
node_start_datetime["out_datetime"] = node_sun["in_start_datetime"]
node_start_datetime["out_datetime"] = node_occlusion["in_start_datetime"]
node_start_datetime["out_datetime"] = node_earth_occlusion["in_start_datetime"]

# solar panel
node_sun["out_solar_constant"] = node_solar_panel["in_solar_constant"]
node_sun["out_sun_unit_icrs"] = node_solar_panel["in_sun_unit_sc"]
node_fs["out_solar_gimbal"] = node_solar_panel["in_sp_gimbal_angle"]
node_occlusion["out_is_occluded"] = node_solar_panel["in_is_occluded"]
node_rb["output_q_sc_to_eci"] = node_solar_panel["input_q_sc2eci"]

# thermals
node_sun["out_solar_constant"] = node_thermal["in_solar_constant"]
node_sun["out_sun_unit_icrs"] = node_thermal["in_sun_unit"]
node_rb["output_q_sc_to_eci"] = node_thermal["in_q_sc2eci"]

# battery
node_solar_panel["out_power"] = node_batt["in_current"]
node_batt_draw["output"] = node_batt["in_current_draw"]

# flight software
node_control["output_q_err"] = node_fs["in_point_err"]
node_nadir_pointing["output_q_cmd"] = node_fs["in_q_cmd_nadir"]
node_ss_pointing["output_q_cmd"] = node_fs["in_q_cmd_ss"]
node_com_pointing["output_q_cmd"] = node_fs["in_q_cmd_com"]
node_cool_pointing["output_q_cmd"] = node_fs["in_q_cmd_cool"]
node_nadir_pointing["output_w_cmd"] = node_fs["in_w_cmd_nadir"]
node_ss_pointing["output_w_cmd"] = node_fs["in_w_cmd_ss"]
node_com_pointing["output_w_cmd"] = node_fs["in_w_cmd_com"]
node_cool_pointing["output_w_cmd"] = node_fs["in_w_cmd_cool"]
node_nadir_pointing["output_gimbal_cmd"] = node_fs["in_gimbal_cmd_nadir"]
node_ss_pointing["output_sp_gimbal"] = node_fs["in_gimbal_cmd_ss"]
node_com_pointing["output_sp_gimbal"] = node_fs["in_gimbal_cmd_com"]
node_cool_pointing["output_sp_gimbal"] = node_fs["in_gimbal_cmd_cool"]
node_thermal["out_sc_temp"] = node_fs["in_sc_temp"]
node_batt["out_soc"] = node_fs["in_sc_soc"]
node_earth_occlusion["out_is_occluded"] = node_fs["in_is_occluded_earth"]
node_occlusion["out_is_occluded"] = node_fs["in_is_occluded_sun"]

node_fs["out_solar_gimbal"] = node_fault_mgs["in_gimbal_cmd"]
node_solar_panel["out_gimbal_measure"] = node_fault_mgs["in_gimbal_measure"]

node_fault_mgs["out_gimbal_fault"] = node_fs["in_is_fault"]

# metrics and viz...
node_orbit["output_r_eci"] = node_orbit_scope["input_r_eci"]
node_orbit["output_v_eci"] = node_orbit_scope["input_v_eci"]
node_sun["out_sun_pos_icrs"] = node_orbit_scope["input_sc_sun_icrs"]
node_earth["out_earth_pos_icrs"] = node_orbit_scope["input_sc_earth_icrs"]
node_earth_occlusion["out_is_occluded"] = node_orbit_scope["input_earth_occ"]
node_occlusion["out_is_occluded"] = node_orbit_scope["input_sun_occ"]
node_start_datetime["out_datetime"] = node_orbit_scope["in_start_datetime"]

node_rb["output_q_sc_to_eci"] = node_att_scope["input_q_sc_to_eci"]
node_rb["output_w_sc"] = node_att_scope["input_w_sc"]
node_nadir_pointing["output_nadir"] = node_att_scope["input_nadir"]
node_solar_panel["out_gimbal"] = node_att_scope["input_gimbal"]
node_sun["out_sun_unit_icrs"] = node_att_scope["input_sun_unit"]
node_earth["out_earth_unit_icrs"] = node_att_scope["input_earth_unit"]
node_fs["out_fs_state"] = node_att_scope["input_fs_state"]
node_earth_occlusion["out_is_occluded"] = node_att_scope["input_earth_occ"]
node_occlusion["out_is_occluded"] = node_att_scope["input_sun_occ"]
node_start_datetime["out_datetime"] = node_att_scope["in_start_datetime"]

node_nadir_pointing["output_nadir"] = node_metric_pointing["input_cmd_vec"]
node_boresight["out_vec"] = node_metric_pointing["input_meas_vec"]
node_start_datetime["out_datetime"] = node_metric_pointing["in_start_datetime"]

node_batt["out_soc"] = node_metric_soc_temp["in_soc"]
node_thermal["out_sc_temp"] = node_metric_soc_temp["in_temp"]
node_start_datetime["out_datetime"] = node_metric_soc_temp["in_start_datetime"]

node_control["output_q_err"] = node_viz_att_cntrl_error["in_q_e"]
node_control["output_w_err"] = node_viz_att_cntrl_error["in_w_e"]

node_fs["out_science"] = node_metric_science["in_science"]
node_fs["out_science_downlinked"] = node_metric_science["in_science_dl"]
node_start_datetime["out_datetime"] = node_metric_science["in_start_datetime"]

if args.fault_config is not None:
    system.add_faults(args.fault_config)

# cProfile.run("system.simulate(3600 * 0.2, 1)")
system.simulate(
    args.sim_duration,
    args.dt,
    save_dir=args.save_dir,
    sim_name=args.sim_name,
    batches=args.batches,
)


"""
Module containing the definition of known / supported "blocks" of the
pcap-ng format.

Each block is a struct-like object with some fields and possibly
a variable amount of "items" (usually options).

They can optionally expose some other properties, used eg. to provide
better access to decoded information, ...
"""

import io
import itertools

from pcapng.structs import (
    struct_decode, RawBytes, IntField, OptionsField, PacketDataField,
    ListField, NameResolutionRecordField, SimplePacketDataField)
from pcapng.constants import link_types
from pcapng.utils import unpack_timestamp_resolution

KNOWN_BLOCKS = {}

class Block(object):
    """Base class for blocks"""

    schema = []

    def __init__(self, raw):
        self._raw = raw
        self._decoded = None

    @classmethod
    def from_context(cls, raw, ctx):
        return cls(raw)  # no context needed by default

    def _decode(self):
        return struct_decode(self.schema, io.BytesIO(self._raw),
                             endianness=self.section.endianness)

    def __getattr__(self, name):
        if self._decoded is None:
            self._decoded = self._decode()
        try:
            return self._decoded[name]
        except KeyError:
            raise AttributeError(name)

    def __repr__(self):
        args = []
        for item in self.schema:
            name = item[0]
            value = getattr(self, name)
            try:
                value = repr(value)
            except:
                value = '<{0} (repr failed)>'.format(type(value).__name__)
            args.append('{0}={1}'.format(name, value))
        return '<{0} {1}>'.format(self.__class__.__name__, ' '.join(args))

class SectionMemberBlock(Block):
    def __init__(self, raw, section):
        super(SectionMemberBlock, self).__init__(raw)
        self.section = section

    @classmethod
    def from_context(cls, raw, ctx):
        return cls(raw, section=ctx.current_section)

def register_block(block):
    """Handy decorator to register a new known block type"""
    KNOWN_BLOCKS[block.magic_number] = block
    return block

@register_block
class SectionHeader(Block):
    magic_number = 0x0a0d0d0a
    schema = [
        ('version_major', IntField(16, False)),
        ('version_minor', IntField(16, False)),
        ('section_length', IntField(64, True)),
        ('options', OptionsField([
            (2, 'shb_hardware', 'string'),
            (3, 'shb_os', 'string'),
            (4, 'shb_userappl', 'string'),
        ]))]

    def __init__(self, raw, endianness):
        self._raw = raw
        self._decoded = None
        self.endianness = endianness
        self._interfaces_id = itertools.count(0)
        self.interfaces = {}
        self.interface_stats = {}

    def _decode(self):
        return struct_decode(self.schema, io.BytesIO(self._raw),
                             endianness=self.endianness)

    def register_interface(self, interface):
        """Helper method to register an interface within this section"""
        assert isinstance(interface, InterfaceDescription)
        interface_id = next(self._interfaces_id)
        interface.interface_id = interface_id
        self.interfaces[interface_id] = interface

    def add_interface_stats(self, interface_stats):
        """Helper method to register interface stats within this section"""
        assert isinstance(interface_stats, InterfaceStatistics)
        self.interface_stats[interface_stats.interface_id] = interface_stats

    @property
    def version(self):
        return (self.version_major, self.version_minor)

    @property
    def length(self):
        return self.section_length

    def __repr__(self):
        return ('<{name} version={version} endianness={endianness} '
                'length={length} options={options}>').format(
            name=self.__class__.__name__,
            version='.'.join(str(x) for x in self.version),
            endianness=repr(self.endianness),
            length=self.length,
            options=repr(self.options))

@register_block
class InterfaceDescription(SectionMemberBlock):
    magic_number = 0x00000001
    schema = [
        ('link_type', IntField(16, False)),  # todo: enc/decode
        ('reserved', RawBytes(2)),
        ('snaplen', IntField(32, False)),
        ('options', OptionsField([
            (2, 'if_name', 'string'),
            (3, 'if_description', 'string'),
            (4, 'if_IPv4addr', 'ipv4+mask'),
            (5, 'if_IPv6addr', 'ipv6+prefix'),
            (6, 'if_MACaddr', 'macaddr'),
            (7, 'if_EUIaddr', 'euiaddr'),
            (8, 'if_speed', 'u64'),
            (9, 'if_tsresol'),  # Just keep the raw data
            (10, 'if_tzone', 'u32'),
            (11, 'if_filter', 'string'),
            (12, 'if_os', 'string'),
            (13, 'if_fcslen', 'u8'),
            (14, 'if_tsoffset', 'i64'),
        ]))]

    @property  # todo: cache this property
    def timestamp_resolution(self):
        # ------------------------------------------------------------
        # Resolution of timestamps. If the Most Significant Bit is
        # equal to zero, the remaining bits indicates the resolution
        # of the timestamp as as a negative power of 10 (e.g. 6 means
        # microsecond resolution, timestamps are the number of
        # microseconds since 1/1/1970). If the Most Significant Bit is
        # equal to one, the remaining bits indicates the resolution as
        # as negative power of 2 (e.g. 10 means 1/1024 of second). If
        # this option is not present, a resolution of 10^-6 is assumed
        # (i.e. timestamps have the same resolution of the standard
        # 'libpcap' timestamps).
        # ------------------------------------------------------------

        if 'if_tsresol' in self.options:
            return unpack_timestamp_resolution(self.options['if_tsresol'])

        return 1e-6

    @property
    def statistics(self):
        # todo: ensure we always have an interface id -> how??
        return self.section.interface_stats.get(self.interface_id)

    @property
    def link_type_description(self):
        try:
            return link_types.LINKTYPE_DESCRIPTIONS[self.link_type]
        except KeyError:
            return 'Unknown link type: 0x{0:04x}'.format(self.link_type)

class BlockWithTimestampMixin(object):
    """
    Block mixin adding properties to better access timestamps
    of blocks that provide one.
    """

    @property
    def timestamp(self):
        # First, get the accuracy from the ts_resol option
        return (((self.timestamp_high << 32) + self.timestamp_low)
                * self.timestamp_resolution)

    @property
    def timestamp_resolution(self):
        return self.interface.timestamp_resolution

    # todo: add some property returning a datetime() with timezone..

class BlockWithInterfaceMixin(object):
    @property
    def interface(self):
        # We need to get the correct interface from the section
        # by looking up the interface_id
        return self.section.interfaces[self.interface_id]

class BasePacketBlock(
        SectionMemberBlock,
        BlockWithInterfaceMixin,
        BlockWithTimestampMixin):
    """Base class for the "EnhancedPacket" and "Packet" blocks"""
    pass

@register_block
class EnhancedPacket(BasePacketBlock):
    magic_number = 0x00000006
    schema = [
        ('interface_id', IntField(32, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('packet_payload_info', PacketDataField()),
        ('options', OptionsField([
            (2, 'epb_flags'),  # todo: is this endianness dependent?
            (3, 'epb_hash'),  # todo: process the hash value
            (4, 'epb_dropcount', 'u64'),
        ]))
    ]

    @property
    def captured_len(self):
        return self.packet_payload_info[0]

    @property
    def packet_len(self):
        return self.packet_payload_info[1]

    @property
    def packet_data(self):
        return self.packet_payload_info[2]

@register_block
class SimplePacket(SectionMemberBlock):
    magic_number = 0x00000003
    schema = [
        ('packet_simple_payload_info', SimplePacketDataField()),
    ]

    @property
    def packet_len(self):
        return self.packet_simple_payload_info[1]

    @property
    def packet_data(self):
        return self.packet_simple_payload_info[2]

@register_block
class Packet(BasePacketBlock):
    magic_number = 0x00000002
    schema = [
        ('interface_id', IntField(16, False)),
        ('drops_count', IntField(16, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('packet_payload_info', PacketDataField()),
        ('options', OptionsField([
            (2, 'epb_flags', 'u32'),  # A flag!
            (3, 'epb_hash'),  # Variable size!
        ]))
    ]

    @property
    def captured_len(self):
        return self.packet_payload_info[0]

    @property
    def packet_len(self):
        return self.packet_payload_info[1]

    @property
    def packet_data(self):
        return self.packet_payload_info[2]

@register_block
class NameResolution(SectionMemberBlock):
    magic_number = 0x00000004
    schema = [
        ('records', ListField(NameResolutionRecordField())),
        ('options', OptionsField([
            (2, 'ns_dnsname', 'string'),
            (3, 'ns_dnsIP4addr', 'ipv4'),
            (4, 'ns_dnsIP6addr', 'ipv6'),
        ])),
    ]

@register_block
class InterfaceStatistics(SectionMemberBlock, BlockWithTimestampMixin,
                          BlockWithInterfaceMixin):
    magic_number = 0x00000005
    schema = [
        ('interface_id', IntField(32, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('options', OptionsField([
            (2, 'isb_starttime', 'u64'),  # todo: consider resolution
            (3, 'isb_endtime', 'u64'),
            (4, 'isb_ifrecv', 'u64'),
            (5, 'isb_ifdrop', 'u64'),
            (6, 'isb_filteraccept', 'u64'),
            (7, 'isb_osdrop', 'u64'),
            (8, 'isb_usrdeliv', 'u64'),
        ])),
    ]

class UnknownBlock(Block):
    """
    Class used to represent an unknown block.

    Its block type and raw data will be stored directly with no further
    processing.
    """

    def __init__(self, block_type, data):
        self.block_type = block_type
        self.data = data

    def __repr__(self):
        return ('UnknownBlock(0x{0:08X}, {1!r})'
                .format(self.block_type, self.data))


# Importando SQLite

import sqlite3 as lite

# Criando conexão do Banco de Dados

con = lite.connect('Lojinha_Moonli.db')

# Tabela criada e desativada pois ja foi criada no meu sistema

with con:
    cur = con.cursor()
    cur.execute("CREATE TABLE Produtos(sku INTEGER PRIMARY KEY, nome TEXT, marca TEXT, preco TEXT, precodez TEXT, quantidade TEXT, garantia TEXT)")


import zmq

def start_server():
    context=zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind("tcp://*:5556")

    while True:
        message=socket.recv_string()
        print("%s" % message)
        socket.send_string("18429 サーバからクライアントに送るメッセージ：絶好調やで")

    socket.close()
    context.destroy()

if __name__ == "__main__":
    start_server()


'''
协程调用同步函数 - call_later
'''
import asyncio
def callback(n):
    print(f'N is {n}')
async def main(loop):
    print('注册Callback')
    loop.call_later(0.2, callback, 1)
    loop.call_later(0.1, callback, 2)
    loop.call_soon(callback, 3)
    await asyncio.sleep(0.4)
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main(loop))
    finally:
        loop.close()


import logging
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Union
import numpy as np
from table_extractor.model.table import (
    BorderBox,
    Cell,
    CellLinked,
    GridCell,
    GridCol,
    GridRow,
    GridTable,
    StructuredTable,
)
from sklearn.cluster import DBSCAN
LOGGER = logging.getLogger(__name__)

@dataclass
class MutableParameter:
    param: int

@dataclass
class MutableBox:
    orig: Union[BorderBox, Cell]
    top_left_x: MutableParameter
    top_left_y: MutableParameter
    bottom_right_x: MutableParameter
    bottom_right_y: MutableParameter

def _match_cells_and_table(table: GridTable, cells: List[Cell]):
    not_matched = []
    for cell in cells:
        row_num = None
        for r_idx, row in enumerate(table.rows):
            if row.box_is_inside_another(cell):
                row_num = r_idx
                break
        col_num = None
        for c_idx, col in enumerate(table.cols):
            if col.box_is_inside_another(cell):
                col_num = c_idx
                break
        if row_num is not None and col_num is not None:
            g_cell = table.cells[row_num * len(table.cols) + col_num]
            if not g_cell.box_is_inside_another(cell):
                not_matched.append(cell)
            g_cell.cells.append(cell)
        else:
            not_matched.append(cell)

    return not_matched

def _merge_closest_cells(g_cells: List[GridCell]):
    for g_cell in g_cells:
        cells = g_cell.cells.copy()
        new_cells = []
        while len(cells) > 0:
            curr_cell = cells.pop()
            for orig_cell in cells:
                if curr_cell.box_is_inside_another(orig_cell, 0.05):
                    curr_cell = curr_cell.merge(orig_cell)
                    cells.remove(orig_cell)
            new_cells.append(curr_cell)
        g_cell.cells = new_cells

def _find_gaps_in_zone(
    line: List[bool], zone: Tuple[int, int]
) -> List[List[int]]:
    gaps = []
    curr_gap = []
    for point_idx in range(zone[0], zone[1]):
        if line[point_idx] and curr_gap:
            curr_gap.append(point_idx - 1)
            gaps.append(curr_gap)
            curr_gap = []
        if not line[point_idx] and not curr_gap:
            curr_gap.append(point_idx)
    if curr_gap:
        curr_gap.append(zone[1])
        gaps.append(curr_gap)
    return gaps

def _get_line_coord_from_gaps(gaps: List[List[int]]) -> List[int]:
    return [(gap[0] + gap[1]) // 2 for gap in gaps]

def find_grid_table(h_lines, v_lines) -> GridTable:
    cells: List[GridCell] = []
    rows: List[GridRow] = []
    cols: List[GridCol] = []
    prev_h_line = h_lines[0]
    for h_idx, h_line in enumerate(h_lines[1:]):
        if h_idx >= len(rows):
            curr_row = GridRow(
                top_left_x=v_lines[0],
                top_left_y=prev_h_line,
                bottom_right_x=v_lines[-1],
                bottom_right_y=h_line,
            )
            rows.append(curr_row)
        else:
            curr_row = rows[h_idx]

        prev_v_line = v_lines[0]
        for v_idx, v_line in enumerate(v_lines[1:]):
            if v_idx >= len(cols):
                curr_col = GridCol(
                    top_left_x=prev_v_line,
                    top_left_y=h_lines[0],
                    bottom_right_x=v_line,
                    bottom_right_y=h_lines[-1],
                )
                cols.append(curr_col)
            else:
                curr_col = cols[v_idx]
            cell = GridCell(
                top_left_x=prev_v_line,
                bottom_right_x=v_line,
                top_left_y=prev_h_line,
                bottom_right_y=h_line,
                row=h_idx,
                col=v_idx,
            )
            curr_row.g_cells.append(cell)
            curr_col.g_cells.append(cell)
            cells.append(cell)
            prev_v_line = v_line
        prev_h_line = h_line
    grid_table = GridTable(rows=rows, cols=cols, cells=cells)
    return grid_table

def _find_lines(
    table_bbox: BorderBox, cells: List[Cell], image_shape: Tuple[int, int]
):
    if not cells:
        return [], []
    h_proj = [False for _ in range(0, image_shape[1])]
    v_proj = [False for _ in range(0, image_shape[0])]

    for cell in cells:
        h_proj[cell.top_left_x : cell.bottom_right_x + 1] = [
            True for _ in range(cell.top_left_x, cell.bottom_right_x + 1)
        ]
        v_proj[cell.top_left_y : cell.bottom_right_y + 1] = [
            True for _ in range(cell.top_left_y, cell.bottom_right_y + 1)
        ]

    v_gaps = _find_gaps_in_zone(
        h_proj, (table_bbox.top_left_x, table_bbox.bottom_right_x)
    )
    v_line_coords = _get_line_coord_from_gaps(v_gaps)

    h_gaps = _find_gaps_in_zone(
        v_proj, (table_bbox.top_left_y, table_bbox.bottom_right_y)
    )
    h_line_coords = _get_line_coord_from_gaps(h_gaps)
    return h_line_coords, v_line_coords

def _actualize_line_separators(
    table: GridTable, image_shape: Tuple[int, int]
) -> Tuple[List[int], List[int]]:
    span_candidates: Dict[int, GridCell] = {}
    for g_cell in table.cells:
        if len(g_cell.cells) > 1:
            span_candidates[len(table.cols) * g_cell.row + g_cell.col] = g_cell

    if not span_candidates:
        return [], []

    col_candidates = {}
    for g_cell in span_candidates.values():
        col_candidates[g_cell.col] = table.cols[g_cell.col]

    row_candidates = {}
    for g_cell in span_candidates.values():
        row_candidates[g_cell.row] = table.rows[g_cell.row]

    v_lines_to_add = []
    h_lines_to_add = []
    for cand_col in col_candidates.values():
        v_lines = []
        for g_cell in cand_col.g_cells:
            _, v_cell_lines = _find_lines(g_cell, g_cell.cells, image_shape)
            if v_cell_lines:
                min_v_cells = min([cell.top_left_x for cell in g_cell.cells])
                max_v_cells = max(
                    [cell.bottom_right_x for cell in g_cell.cells]
                )
                v_cell_lines = list(
                    filter(
                        lambda line: min_v_cells < line < max_v_cells,
                        v_cell_lines,
                    )
                )
            v_lines.append(v_cell_lines)
        g_cell_v_line = list(zip(cand_col.g_cells, v_lines))
        cand_v_sort = list(
            filter(
                lambda x: x[3],
                sorted(
                    [
                        (idx, len(v_cell_lines), g_cell, v_cell_lines)
                        for idx, (g_cell, v_cell_lines) in enumerate(
                            g_cell_v_line
                        )
                    ],
                    key=lambda x: (x[1], x[2].top_left_y),
                ),
            )
        )

        i = 0
        while i < len(cand_v_sort):
            idx, l, g_cell, v_cell_lines = cand_v_sort[i]
            if not l:
                i += 1
                continue
            cand_g_cells = g_cell.cells.copy()
            new_v_lines = v_cell_lines
            count_not_broken = 0
            for j in range(i + 1, len(cand_v_sort)):
                jdx, _, cand_j, v_lines_j = cand_v_sort[j]
                # Try compute common v_lines
                cells_to_check = cand_g_cells.copy()
                cells_to_check.extend(cand_j.cells)
                zone = BorderBox(
                    top_left_x=g_cell.top_left_x,
                    top_left_y=g_cell.top_left_y,
                    bottom_right_x=g_cell.bottom_right_x,
                    bottom_right_y=cand_j.bottom_right_y,
                )
                _, v = _find_lines(zone, cells_to_check, image_shape)
                if v:
                    min_v_cells = min(
                        [cell.top_left_x for cell in cells_to_check]
                    )
                    max_v_cells = max(
                        [cell.bottom_right_x for cell in cells_to_check]
                    )
                    v = list(
                        filter(
                            lambda line: min_v_cells < line < max_v_cells, v
                        )
                    )
                if len(v) >= len(v_cell_lines):
                    cand_g_cells = cells_to_check
                    new_v_lines = v
                    count_not_broken += 1
                else:
                    break
            i += count_not_broken + 1
            v_lines_to_add.extend(new_v_lines)

    for cand_row in row_candidates.values():
        h_lines = []
        for g_cell in cand_row.g_cells:
            h_cell_lines, _ = _find_lines(g_cell, g_cell.cells, image_shape)
            if h_cell_lines:
                min_h_cells = min([cell.top_left_y for cell in g_cell.cells])
                max_h_cells = max(
                    [cell.bottom_right_y for cell in g_cell.cells]
                )
                h_cell_lines = list(
                    filter(
                        lambda line: min_h_cells < line < max_h_cells,
                        h_cell_lines,
                    )
                )
            h_lines.append(h_cell_lines)
        g_cell_h_line = list(zip(cand_row.g_cells, h_lines))
        cand_h_sort = sorted(
            [
                (idx, len(h_cell_lines), g_cell, h_cell_lines)
                for idx, (g_cell, h_cell_lines) in enumerate(g_cell_h_line)
            ],
            key=lambda x: (x[1], x[2].top_left_y),
        )

        i = 0
        while i < len(cand_h_sort):
            idx, l, g_cell, h_cell_lines = cand_h_sort[i]
            if not l:
                i += 1
                continue
            cand_g_cells = g_cell.cells.copy()
            new_h_lines = h_cell_lines
            count_not_broken = 0
            for j in range(i + 1, len(cand_h_sort)):
                jdx, _, cand_j, h_lines_j = cand_h_sort[j]
                # Try compute common v_lines
                cells_to_check = cand_g_cells.copy()
                cells_to_check.extend(cand_j.cells)
                zone = BorderBox(
                    top_left_x=g_cell.top_left_x,
                    top_left_y=g_cell.top_left_y,
                    bottom_right_x=g_cell.bottom_right_x,
                    bottom_right_y=cand_j.bottom_right_y,
                )
                h, _ = _find_lines(zone, cells_to_check, image_shape)
                if h:
                    min_h_cells = min(
                        [cell.top_left_y for cell in cells_to_check]
                    )
                    max_h_cells = max(
                        [cell.bottom_right_y for cell in cells_to_check]
                    )
                    h = list(
                        filter(
                            lambda line: min_h_cells < line < max_h_cells, h
                        )
                    )
                if len(h) >= len(h_cell_lines):
                    cand_g_cells = cells_to_check
                    new_h_lines = h
                    count_not_broken += 1
                else:
                    break
            i += count_not_broken + 1
            h_lines_to_add.extend(new_h_lines)
    return list(set(v_lines_to_add)), list(set(h_lines_to_add))

def reconstruct_table_from_grid(
    grid_table: GridTable, cells: List[Cell]
) -> Tuple[Optional[StructuredTable], List[Cell]]:
    not_matched = []
    linked_cells = []
    grid_cells_dict = {}
    for g_cell in grid_table.cells:
        grid_cells_dict[
            g_cell.row * len(grid_table.cols) + g_cell.col
        ] = g_cell
    for cell in cells:
        rows = []
        for r_idx, row in enumerate(grid_table.rows):
            if row.box_is_inside_another(cell, 0.0):
                rows.append((r_idx, row))
        cols = []
        for c_idx, col in enumerate(grid_table.cols):
            if col.box_is_inside_another(cell, 0.0):
                cols.append((c_idx, col))
        matched_cells = []
        for row in rows:
            for col in cols:
                if grid_cells_dict.get(row[0] * len(grid_table.cols) + col[0]):
                    matched_cells.append(grid_cells_dict.pop(row[0] * len(grid_table.cols) + col[0]))
        if matched_cells:
            linked_cells.append(
                CellLinked(
                    top_left_y=min([c.top_left_y for c in matched_cells]),
                    top_left_x=min([c.top_left_x for c in matched_cells]),
                    bottom_right_y=max([c.bottom_right_y for c in matched_cells]),
                    bottom_right_x=max([c.bottom_right_x for c in matched_cells]),
                    row=min([c.row for c in matched_cells]),
                    col=min([c.col for c in matched_cells]),
                    row_span=max([c.row for c in matched_cells]) - min([c.row for c in matched_cells]) + 1,
                    col_span=max([c.col for c in matched_cells]) - min([c.col for c in matched_cells]) + 1,
                    text_boxes=cell.text_boxes,
                )
            )
        else:
            not_matched.append(cell)
    for _, g_cell in grid_cells_dict.items():
        linked_cells.append(
            CellLinked(
                top_left_y=g_cell.top_left_y,
                top_left_x=g_cell.top_left_x,
                bottom_right_y=g_cell.bottom_right_y,
                bottom_right_x=g_cell.bottom_right_x,
                row=g_cell.row,
                col=g_cell.col,
                row_span=1,
                col_span=1,
                text_boxes=[],
            )
        )
    if not grid_table.cols or not grid_table.rows or not grid_table.cells:
        return None, cells
    table = StructuredTable(
        bbox=BorderBox(
            top_left_y=grid_table.rows[0].top_left_y,
            top_left_x=grid_table.cols[0].top_left_x,
            bottom_right_y=grid_table.rows[-1].bottom_right_y,
            bottom_right_x=grid_table.cols[-1].bottom_right_x,
        ),
        cells=linked_cells,
    )
    return table, not_matched

def construct_table_from_cells(
    table_bbox: BorderBox, cells: List[Cell], image_shape: Tuple[int, int]
) -> Optional[StructuredTable]:
    if not table_bbox or not image_shape or not cells or len(cells) < 2:
        return None
    _ = _get_lines(cells, table_bbox)

    table_bbox.bottom_right_x = table_bbox.bottom_right_x + 2
    table_bbox.bottom_right_y = table_bbox.bottom_right_y + 2
    table_bbox.top_left_x = table_bbox.top_left_x - 2
    table_bbox.top_left_y = table_bbox.top_left_y - 2

    h_lines, v_lines = _find_lines(table_bbox, cells, image_shape)
    if not h_lines:
        h_lines = [table_bbox.top_left_y, table_bbox.bottom_right_y]
    elif len(h_lines) == 1:
        h_lines = (
            [table_bbox.top_left_y] + h_lines + [table_bbox.bottom_right_y]
        )
    if not v_lines:
        v_lines = [table_bbox.top_left_x, table_bbox.bottom_right_x]
    elif len(v_lines) == 1:
        v_lines = (
            [table_bbox.top_left_x] + v_lines + [table_bbox.bottom_right_x]
        )

    grid_table = find_grid_table(h_lines, v_lines)

    while True:
        _match_cells_and_table(grid_table, cells)
        vv_lines, hh_lines = _actualize_line_separators(
            grid_table, image_shape
        )
        if not vv_lines and not hh_lines:
            break
        h_lines.extend(hh_lines)
        v_lines.extend(vv_lines)
        grid_table = find_grid_table(sorted(h_lines), sorted(v_lines))

    table, _ = reconstruct_table_from_grid(grid_table, cells)
    if _:
        LOGGER.debug(f"Not matched: {len(_)}")
    return table

def cluster(coords: List[MutableParameter], eps=3, min_samples=1):
    if len(coords) < 3:
        return coords
    values = [coord.param for coord in coords]
    np_coords = np.array(values).reshape(-1, 1)
    clust = DBSCAN(eps=eps, min_samples=min_samples).fit(np_coords)
    lines = {}
    for coord, label in zip(coords, clust.labels_):
        if label in lines:
            lines[label].append(coord)
        else:
            lines[label] = [coord]
    lines_p = []
    for l_coords in lines.values():
        avg = int(sum([coord.param for coord in l_coords]) / len(l_coords))
        lines_p.append(avg)
        for coord in l_coords:
            coord.param = avg
    return lines_p

def to_mutable(bbox: BorderBox):
    return MutableBox(
        orig=bbox,
        top_left_x=MutableParameter(bbox.top_left_x),
        top_left_y=MutableParameter(bbox.top_left_y),
        bottom_right_x=MutableParameter(bbox.bottom_right_x),
        bottom_right_y=MutableParameter(bbox.bottom_right_y)
    )

def update_coords(bbox: MutableBox):
    bbox.orig.top_left_x = bbox.top_left_x.param + 1
    bbox.orig.top_left_y = bbox.top_left_y.param + 1
    bbox.orig.bottom_right_x = bbox.bottom_right_x.param - 1
    bbox.orig.bottom_right_y = bbox.bottom_right_y.param - 1

def _get_lines(cells: List[Cell], table_bbox: BorderBox):
    table_bbox_m = to_mutable(table_bbox)
    cells_m = [to_mutable(cell) for cell in cells]

    h_coords = [table_bbox_m.top_left_y, table_bbox_m.bottom_right_y]
    v_coords = [table_bbox_m.top_left_x, table_bbox_m.bottom_right_x]
    for cell in cells_m:
        v_coords.extend([cell.top_left_x, cell.bottom_right_x])
        h_coords.extend([cell.top_left_y, cell.bottom_right_y])

    v_l = cluster(v_coords)
    h_l = cluster(h_coords)

    update_coords(table_bbox_m)
    for cell in cells_m:
        update_coords(cell)

    return v_l, h_l


import unittest
import tempfile
import os
import numpy as np
#Internal libraries
from miscnn import Data_IO
from miscnn.data_loading.interfaces import Dictionary_interface

#-----------------------------------------------------#
#                  Unittest: Data IO                  #
#-----------------------------------------------------#
class Data_IOTEST(unittest.TestCase):
    # Create random imaging and segmentation data
    @classmethod
    def setUpClass(self):
        # Create imgaging and segmentation data set
        np.random.seed(1234)
        self.dataset = dict()
        for i in range(0, 10):
            img = np.random.rand(16, 16, 16) * 255
            self.img = img.astype(int)
            seg = np.random.rand(16, 16, 16) * 3
            self.seg = seg.astype(int)
            if i == 3 : sample = (self.img, self.seg, self.seg)
            elif i == 5 : sample = (self.img, None, self.seg)
            else : sample = (self.img, self.seg)
            self.dataset["TEST.sample_" + str(i)] = sample
        # Initialize Dictionary IO Interface
        self.io_interface = Dictionary_interface(self.dataset)
        # Initialize temporary directory
        self.tmp_dir = tempfile.TemporaryDirectory(prefix="tmp.miscnn.")
        self.tmp_batches = os.path.join(self.tmp_dir.name, "batches")

    # Delete all temporary files
    @classmethod
    def tearDownClass(self):
        self.tmp_dir.cleanup()

    #-------------------------------------------------#
    #                Base Functionality               #
    #-------------------------------------------------#
    # Class Creation
    def test_DATAIO_BASE_create(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)

    # Obtain index list
    def test_DATAIO_BASE_getIndexList(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample_list = data_io.get_indiceslist()
        self.assertEqual(len(sample_list), 10)
        self.assertIn("TEST.sample_0", sample_list)

    # Obtain sample list
    def test_DATAIO_BASE_getSampleList(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample_list = data_io.get_samples()
        self.assertEqual(len(sample_list), 10)
        for i, sample in enumerate(sample_list):
            self.assertEqual("TEST.sample_" + str(i), sample.index)

    # Prediction storage
    def test_DATAIO_BASE_savePrediction(self):
        data_io = Data_IO(self.io_interface, input_path="",
                         output_path=os.path.join(self.tmp_dir.name, "pred"),
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        self.assertIsNone(sample.pred_data)
        sample.pred_data = sample.seg_data
        data_io.save_prediction(sample)
        self.assertTrue(os.path.exists(os.path.join(self.tmp_dir.name, "pred")))
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=True)
        self.assertTrue(np.array_equal(sample.seg_data, sample.pred_data))

    #-------------------------------------------------#
    #                  Sample Loader                  #
    #-------------------------------------------------#
    # Sample Loader - Imaging
    def test_DATAIO_SampleLoader_Imaging(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=False, load_pred=False)
        self.assertTrue(np.array_equal(np.reshape(sample.img_data, (16,16,16)),
                                       self.dataset["TEST.sample_0"][0]))
        self.assertEqual(sample.img_data.shape, (16, 16, 16, 1))

    # Sample Loader - Segmentation
    def test_DATAIO_SampleLoader_Segmentation(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        self.assertTrue(np.array_equal(np.reshape(sample.seg_data, (16,16,16)),
                                       self.dataset["TEST.sample_0"][1]))
        self.assertEqual(sample.seg_data.shape, (16, 16, 16, 1))
        self.assertIsNotNone(sample.img_data)
        self.assertIsNone(sample.pred_data)
        with self.assertRaises(Exception):
            sample = data_io.sample_loader("TEST.sample_5", backup=False,
                                           load_seg=True, load_pred=False)

    # Sample Loader - Prediction
    def test_DATAIO_SampleLoader_Prediction(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_5", backup=False,
                                       load_seg=False, load_pred=True)
        self.assertTrue(np.array_equal(np.reshape(sample.pred_data, (16,16,16)),
                                       self.dataset["TEST.sample_5"][2]))
        self.assertEqual(sample.pred_data.shape, (16, 16, 16, 1))
        self.assertIsNotNone(sample.img_data)
        self.assertIsNone(sample.seg_data)
        with self.assertRaises(Exception):
            sample = data_io.sample_loader("TEST.sample_2", backup=False,
                                           load_seg=False, load_pred=True)

    # Sample Loader - Complete
    def test_DATAIO_SampleLoader_Combined(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_3", backup=False,
                                       load_seg=True, load_pred=True)
        self.assertIsNotNone(sample.img_data)
        self.assertIsNotNone(sample.seg_data)
        self.assertIsNotNone(sample.pred_data)
        self.assertEqual(sample.img_data.shape, sample.seg_data.shape)
        self.assertEqual(sample.seg_data.shape, sample.pred_data.shape)

    #-------------------------------------------------#
    #                 Batch Management                #
    #-------------------------------------------------#
    # Batch Storage
    def test_DATAIO_BATCHES_backup(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        data_io.backup_batches(sample.img_data, sample.seg_data, "abc")
        self.assertEqual(len(os.listdir(self.tmp_batches)), 2)
        data_io.batch_cleanup()

    # Batch Loading
    def test_DATAIO_BATCHES_loading(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        data_io.backup_batches(sample.img_data, sample.seg_data, "abc")
        img = data_io.batch_load(pointer="abc", img=True)
        self.assertTrue(np.array_equal(sample.img_data, img))
        seg = data_io.batch_load(pointer="abc", img=False)
        self.assertTrue(np.array_equal(sample.seg_data, seg))
        data_io.batch_cleanup()

    # Batch Cleanup
    def test_DATAIO_BATCHES_cleanup(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        data_io.backup_batches(sample.img_data, sample.seg_data, "abc")
        data_io.backup_batches(sample.img_data, sample.seg_data, "def")
        data_io.backup_batches(sample.img_data, None, pointer="ghi")
        self.assertEqual(len(os.listdir(self.tmp_batches)), 5)
        data_io.batch_cleanup(pointer="def")
        self.assertEqual(len(os.listdir(self.tmp_batches)), 3)
        data_io.batch_cleanup()
        self.assertEqual(len(os.listdir(self.tmp_batches)), 0)

    # Sample Storage
    def test_DATAIO_BATCHES_sampleStorage(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        data_io.backup_sample(sample)
        self.assertEqual(len(os.listdir(self.tmp_batches)), 1)
        data_io.batch_cleanup()

    # Sample Loading
    def test_DATAIO_BATCHES_sampleLoading(self):
        data_io = Data_IO(self.io_interface, input_path="", output_path="",
                         batch_path=self.tmp_batches, delete_batchDir=False)
        sample = data_io.sample_loader("TEST.sample_0", backup=False,
                                       load_seg=True, load_pred=False)
        data_io.backup_sample(sample)
        sample_new = data_io.load_sample_pickle(sample.index)
        data_io.batch_cleanup()
        self.assertTrue(np.array_equal(sample_new.img_data, sample.img_data))
        self.assertTrue(np.array_equal(sample_new.seg_data, sample.seg_data))

#-----------------------------------------------------#
#               Unittest: Main Function               #
#-----------------------------------------------------#
if __name__ == '__main__':
    unittest.main()


import collections

def count(chessBoard):
    res = collections.defaultdict(int)
    row, col = len(chessBoard), len(chessBoard[0])
    dp = [[0] * col for _ in range(row)]
    for c in range(col):
        if chessBoard[0][c]:
            dp[0][c] = 1
    for r in range(row):
        if chessBoard[r][0]:
            dp[r][0] = 1
    for r in range(1, row):
        for c in range(1, col):
            if chessBoard[r][c]:
                dp[r][c] = min(dp[r - 1][c], dp[r - 1][c - 1], dp[r][c - 1]) + 1

    for r in range(row):
        for c in range(col):
            if dp[r][c] > 1:
                for i in range(dp[r][c], 1, -1):
                    res[i] += 1

    return res

board = [[0, 1, 1, 1, 1],
         [1, 1, 1, 1, 1],
         [1, 1, 1, 1, 1],
         [0, 1, 1, 0, 1],
         [1, 1, 1, 1, 1]]
print(count(board))

completed/Count Squares In the Chess Board.py


from flask import Flask, render_template, request
from secrets import token_hex

import azure_utils

app = Flask(__name__)
app.config['SECRET_KEY'] = token_hex()
# app.config['UPLOADED_PHOTOS_DEST'] = 'uploads'

@app.route('/')
@app.route('/index')
def index():
    return render_template("index.html")

@app.post('/upload')
def post_upload():
    eye_scan_image = request.files['eye_scan_image'].read()
    prediction = azure_utils.get_prediction(eye_scan_image)
    return render_template("show_pred.html")

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


import numpy as np
import cv2
from scipy.odr import Model, ODR, Data

from sklearn import linear_model
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

def orthoregress(x, y, ord):
    """Perform an Orthogonal Distance Regression on the given data,
    using the same interface as the standard scipy.stats.linregress function.
    Arguments:
    x: x data (input)
    y: y data (response)
    Returns:

    a[], where: a[0]*x^ord + a[1]*x^(1) + .... + a[ord]

    Uses standard ordinary least squares to estimate the starting parameters
    then uses the scipy.odr interface to the ODRPACK Fortran code to do the
    orthogonal distance calculations.

    Source: http://blog.rtwilson.com/orthogonal-distance-regression-in-python/
    """
    poly_fit = np.polyfit(x, y, ord)
    # mod = Model(f)
    mod = Model(lambda pp, xx:poly_eval(pp, xx, ord))
    dat = Data(x, y)
    od = ODR(data = dat, model = mod, beta0=poly_fit[0:ord + 1], maxit=10)
    out = od.run()
    return out.beta

def poly_eval(p,x,ord):
    """
    Polynomial evaluation
    """
    val = np.zeros(shape=x.shape, dtype=p.dtype)

    for i in range(0, ord + 1):
        val += p[i]*(np.power(x, ord - i))

    # return (p[0] * x * x) + p[1] * x + p[2]
    return val

def RANSAC_Fit(x, y, ord):
    """
        Fit a n-th order polynomial to a 2D set of datapoints. X is the input, Y is the dependent
        variable.
    :param x: input variable
    :param y: dependent variable
    :param ord: linear order of polynomial
    :return:
    polynomial coefficients 'a' as: a[0]*x^ord + a[1]*x^(ord - 1) + .... + a[ord]
    """
    min_x = np.int64(np.floor(np.min(x)))
    max_x = np.int64(np.ceil(np.max(x)))
    min_y = np.int64(np.floor(np.min(y)))
    max_y = np.int64(np.ceil(np.max(y)))

    # Fit line using all data
    # model = linear_model.LinearRegression()

    model = make_pipeline(PolynomialFeatures(ord), Ridge(alpha=5.0, fit_intercept = True))
    # model.fit(x, y)

    # model = Pipeline([('poly', PolynomialFeatures(degree=ord)), ...
    #                   ('linear', LinearRegression(fit_intercept=True))])

    # model = make_pipeline(PolynomialFeatures(ord), LinearRegression(fit_intercept=True))

    # Robustly fit linear model with RANSAC algorithm
    model_ransac = linear_model.RANSACRegressor(base_estimator=model)
    model_ransac.fit(np.expand_dims(x, axis=1), np.expand_dims(y, axis=1))

    # inlier_mask = model_ransac.inlier_mask_
    # outlier_mask = np.logical_not(inlier_mask)

    # Compute the points generated within the x-range (min/max)
    x_range = np.array(range(min_x, max_x + 1))
    x_range = x_range.reshape(len(x_range), -1)

    # Predict data of estimated models
    y_range = np.array(np.around(model_ransac.predict(x_range), decimals=0), dtype=np.int64)

    # Fit an 'order' polynomial
    return orthoregress(x, y, ord)

def lanes_ok(LRfits, var):
    """
     !!! Expects scaled fits !!!
    Check if the generated lanes are OK.
    Three conditions are checked:
    1 - Curvatures are similar
    2 - Parallel
    3 - Average distance between the lines is within expectations

    :param fits - tuple pairs of left/right fits scaled to world measurements
    :param var - independent variable range to check (the y-variable)
    :return:
    OK - boolean if detected lanes make sense
    reason - reason for failure. Empty list if OK == True
    """
    OK = True
    reason = []

    left_fit = LRfits[0]
    right_fit = LRfits[1]

    assert len(left_fit) == len(right_fit)

    # fit is a polynomial as: a0 * x^(ord) + a1 * x^(ord - 1) + ... + aord
    ord = len(left_fit) - 1

    leftxrange = poly_eval(left_fit, var, ord=ord)
    leftcurvature = curvature(left_fit, var)
    rightxrange = poly_eval(right_fit, var, ord=ord)
    rightcurvature = curvature(right_fit, var)

    # Are lane curvatures similar ?
    # If radius of curvature is very large, lines are fairly straight, hence
    # they can be considered parallel (in the straight sense). If, however, the radius
    # of curvature is short, that means that there is significant turn, in which case
    # the range of curvature should be considered

    # if (np.mean(leftcurvature) > 10000.) and (np.mean(rightcurvature) > 10000.):
    #     # Very large radius, low curvature, both can be considered straight and parallel
    #     pass
    # else:
    #     # How large is the difference between curvatures
    #     dc = np.abs(leftcurvature - rightcurvature)
    #     cdmean = np.mean(dc)
    #     # If difference is in order of magnitude
    #     if cdmean > 10000.:
    #         OK = False
    #         reason.append('curvature')

    # Distances between lines representing lanes
    dh = np.abs(leftxrange - rightxrange)

    # Are the lanes parallel ?
    # Parallel check: is the distance between horizontal positions about the same
    # along the vertical positions (distance from the vehicle)
    min_dh = np.min(dh)
    max_dh = np.max(dh)
    # Largest deviation is greater than 1/10 th of the
    # lane width of about 5m
    # TBD !!!
    # if max_dh - min_dh > 0.5:
    #     OK = False
    #     reason.append('parallel')

    # Is the avg distance between the lanes within expectations ?
    # Expected minimum lane width in the US is 3.7m
    # Interstates have
    # Check that avg. lanes width falls within a certain window
    avgdist = np.mean(dh)
    if (avgdist < 3.5) or (avgdist > 5.2):
        OK = False
        reason.append('lane_width')

    return OK, reason

def radius_of_curvature(a, val):
    """
    Find the radius of curvature of a line, valued at 'val', of a line represented
    by as second order polynomial. The formula used:

    Line: y = f(a, val) = a[0] * val ^ 2 + a[1] * val + a[2]

    Rad of curvature: R(val) = {(1 + (df(a, val)/d_val) ^ 2 ) ^ (3/2)} / {abs( df2(a, val)/d2_val )}

    which results into:

    R = {(1 + (2*a[0]*val + a[1]) ^ 2) ^ (3/2)}  / abs(2 * a[0])

    :param a - 2nd order polynomial coefficients of form a[0] *x^2 + a[1] * x + a[2]
    :param val - value to evaluate the radius of curvature at
    :return:
    R - radius of curvature
    """

    m0 = (2*a[0]*val+a[1]) ** 2
    m1 = np.power(1 + m0, 1.5)
    R = m1/np.abs(2 * a[0])

    return R

def curvature(a, paramvals):
    """
    Generate a sequence of radii of curvature for a n-th order polynomial
    defined line, along its dependent parameter
    :param a: polynomial line parameters, defined as
    a[0] * paramval^n + a[1] * paramval^(n-1) + ... + a[n]
    :param paramvals: np array of parametervalues
    :return:
    array of radii of curvatures along the parameter values
    """
    r = np.zeros_like(paramvals)
    nsamps = np.max(np.size(paramvals))
    for i in range(0, int(nsamps)):
        r[i] = radius_of_curvature(a, paramvals[i])

    return r

def genfit(nonzero, inds, ord):
    """
    Generate fits for the non-zero pixels at desired indeces
    :param nonzero: pair (tuple) of same length arrays of non-zero pixel locations
                    (independent_var[], dependent_var[])
    :param inds: indeces of entries we want to generate fits for
    :param ord: order of polynomial fit
    :return:
    polynomial fit 'a' where: a[0] * independentvar ^ ord + ... + a[ord]
    """

    independentvar = nonzero[0][inds]
    dependentvar = nonzero[1][inds]

    # Fit a second order polynomial to each
    # *
    # fit = np.polyfit(independentvar, dependentvar, ord)
    # *
    fit = orthoregress(independentvar, dependentvar, ord)
    # *
    # fit = RANSAC_Fit(independentvar, dependentvar, ord)

    return fit

def center_offset(center_fit, vehctr, indepvarmax):
    """
    Compute the center offset from the center of the lane.
    Center of lane computed as the polynomial of center_fit @ ymax
    where ymax corresponds to the current position of the vehicle
    along the visible lanes. This is the bottom of the camera image.

    To the left the offset is negative, to the right, the offset is positive.
    The returned scale depends on the input values.
    !! Note !!
    The function will compute blindly the values. Client code must
    make sure that the fit and the scales of the values are correct
    :param center_fit: center lane fit
    :param vehctr: vehicle center (in pixel of vehicle coordinates)
    :param indepvarmax: bottom of image (in pixel or scaled coordinates)
    :return:
    offset measured in input values scale
    """

    ord = len(center_fit) - 1
    lane_ctr = poly_eval(center_fit, indepvarmax, ord)
    # If vehicle center is to the left of lane center, the offset value
    # will be negative, otherwise positive (or zero if values match)
    return vehctr - lane_ctr

def center_fit(fits, independvarrange, ord):
    """
    Find the center fit between L/R fits. The center fit will be calculated
    by fitting a a polynomial to the indepenent variables as (Left + Right) / 2
    then the calculated mid-points (dependent vars) along with the independent
    variable will be used to obtain a new fit
    :param fits: tuple pair of Left & Right fits
    :param independvarrange: range of indepenent variables (numpy array)
    :return:
    middle_fit - mid-fit of the two input fits
    """

    left_fit = fits[0]
    right_fit = fits[1]
    left_poly_vals = poly_eval(left_fit, independvarrange, ord)
    right_poly_vals = poly_eval(right_fit, independvarrange, ord)
    mid_dependent_vars = (right_poly_vals + left_poly_vals)/2.0

    # Simple
    middle_fit = np.polyfit(independvarrange, mid_dependent_vars, ord)

    # middle_fit = RANSAC_Fit(independvarrange, mid_dependent_vars, ord)

    return middle_fit

def compute_fits(imgshape, nonzerox, nonzeroy, left_lane_inds, right_lane_inds, xscale, yscale, ord):
    """
    Compute the left, middle, and right fits of lanes
    :param imgshape:
    :param nonzerox:
    :param nonzeroy:
    :param left_lane_inds:
    :param right_lane_inds:
    :param xscale:
    :param yscale:
    :param ord:
    :return:
    """

    # y is our independent variable here
    left_fit = genfit((nonzeroy, nonzerox), left_lane_inds, ord=ord)
    right_fit = genfit((nonzeroy, nonzerox), right_lane_inds, ord=ord)
    left_fit_scaled = genfit((nonzeroy * yscale, nonzerox * xscale), left_lane_inds, ord=ord)
    right_fit_scaled = genfit((nonzeroy * yscale, nonzerox * xscale), right_lane_inds, ord=ord)

    # Mid-lane fit
    yrange = np.linspace(0, imgshape[0] - 1, imgshape[0])
    yrange_scaled = yrange * yscale
    middle_fit = center_fit((left_fit, right_fit), independvarrange=yrange, ord=ord)
    middle_fit_scaled = center_fit((left_fit_scaled, right_fit_scaled), independvarrange=yrange_scaled, ord=ord)

    return left_fit, left_fit_scaled, right_fit, right_fit_scaled, middle_fit, middle_fit_scaled

def sliding_window_lanes(img, nwindows=9, xscale=1, yscale=1, ord = 2):
    """
        Use sliding windows (vertically) to find lane pixels.
         Fit a second order polynomial nto the found pixels.
         Scale polynomials to input x/y scaling factors (e.g. to world
         measures)
    :param img: binary input image
    :param nwindows: windows to scan upwards along the line points
    :param xscale / yscale : pixel to other measure (for example world)
    :return:
    out_img -  image with rectangles drawn on it (for reference and visualization)
    left_fit, left_fit_scaled, - pixel/scaled fits for left lane
    right_fit, right_fit_scaled - pixel/scale fits for right lane
    nonzerox, nonzeroy - paired arrays for locations of non-zero pixels
    left_lane_inds, right_lane_inds - indeces of pixels belonging to the lanes in the "nonzerox, nonzeroy" pairs
    """

    # Order of polynomial fit
    out_img, nonzerox, nonzeroy, left_lane_inds, right_lane_inds = \
        sliding_window_lane_pixels(img, nwindows)

    left_fit, left_fit_scaled, right_fit, right_fit_scaled, middle_fit, middle_fit_scaled = \
    compute_fits(img.shape, nonzerox, nonzeroy, left_lane_inds, right_lane_inds, xscale, yscale, ord)

    # Compute offset from center
    yrange = np.linspace(0, img.shape[0] - 1, img.shape[0])
    yrange_scaled = yrange * yscale
    # ctr_offset = center_offset(middle_fit_scaled, np.float64(img.shape[1]) * xscale / 2., np.max(yrange_scaled))

    # Do the scaled measurements make sense ?
    OK, reason = lanes_ok((left_fit_scaled, right_fit_scaled), var=yrange_scaled)

    return out_img, left_fit, left_fit_scaled, right_fit, right_fit_scaled, \
           middle_fit, middle_fit_scaled, \
           OK, reason

def sliding_window_lane_pixels(img, nwindows):
    """
        Find the lanes in an image. This function assumes that the center of the lanes is
         around the center of the image. A moving vertically sliding window is implemented
         to search the lane pixels.

    :param img: binary image to search lanes in.
    :param nwindows: number of windows (vertical count, i.e. along the y/height axis)
    :return:
    out_img - image with the windows painted on it (for visualization purposes)
    nonzerox, nonzeroy - the pair lane-pixel coordinates
    left_lane_inds, right_lane_inds - left / right lane indeces of non-zero pixels
    """

    # Take a histogram of the bottom half of the image
    histogram = np.sum(img[np.int(img.shape[0] / 2):, :], axis=0)

    # Create an output image to draw on and  visualize the result
    out_img = np.dstack((img, img, img)) * 255

    # Find the peak of the left and right halves of the histogram
    # These will be the starting point for the left and right lines
    midpoint = np.int(histogram.shape[0] / 2)
    leftx_base = np.argmax(histogram[:midpoint])
    rightx_base = np.argmax(histogram[midpoint:]) + midpoint

    # Set height of windows
    window_height = np.int(img.shape[0] / nwindows)

    # Identify the x and y positions of all nonzero pixels in the image
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])

    # Current positions to be updated for each window
    leftx_current = leftx_base
    rightx_current = rightx_base

    # Set the width of the windows +/- margin
    margin = 100
    # Set minimum number of pixels found to recenter window
    minpix = 50

    # Create empty lists to receive left and right lane pixel indices
    left_lane_inds = []
    right_lane_inds = []

    # Step through the windows one by one and search for pixels that belong to
    # the lanes
    for window in range(nwindows):
        # Identify window boundaries in x and y (and right and left)
        win_y_low = img.shape[0] - (window + 1) * window_height
        win_y_high = img.shape[0] - window * window_height
        win_xleft_low = leftx_current - margin
        win_xleft_high = leftx_current + margin
        win_xright_low = rightx_current - margin
        win_xright_high = rightx_current + margin

        # Draw the windows on the visualization image
        cv2.rectangle(out_img, (win_xleft_low, win_y_low), (win_xleft_high, win_y_high), (0, 255, 0), 2)
        cv2.rectangle(out_img, (win_xright_low, win_y_low), (win_xright_high, win_y_high), (0, 255, 0), 2)

        # Identify the nonzero pixels in x and y within the window
        good_left_inds = ((nonzeroy >= win_y_low) &
                          (nonzeroy < win_y_high) &
                          (nonzerox >= win_xleft_low) &
                          (nonzerox < win_xleft_high)).nonzero()[0]
        good_right_inds = ((nonzeroy >= win_y_low) &
                           (nonzeroy < win_y_high) &
                           (nonzerox >= win_xright_low) &
                           (nonzerox < win_xright_high)).nonzero()[0]

        # Append these indices to the lists
        left_lane_inds.append(good_left_inds)
        right_lane_inds.append(good_right_inds)

        # If you found > minpix pixels, recenter next window on their mean position
        if len(good_left_inds) > minpix:
            leftx_current = np.int(np.mean(nonzerox[good_left_inds]))
        if len(good_right_inds) > minpix:
            rightx_current = np.int(np.mean(nonzerox[good_right_inds]))

    # Concatenate the arrays of indices
    left_lane_inds = np.concatenate(left_lane_inds)
    right_lane_inds = np.concatenate(right_lane_inds)

    # Paint non-zero pixels, belonging to the line-windows with colors
    out_img[nonzeroy[left_lane_inds], nonzerox[left_lane_inds]] = [255, 0, 0]
    out_img[nonzeroy[right_lane_inds], nonzerox[right_lane_inds]] = [0, 0, 255]

    return out_img, nonzerox, nonzeroy, left_lane_inds, right_lane_inds

def lane_pixels_indeces_around_fit(nonzero, fits, ord, margin = 100):
    """
    Find pixels of lanes around a L/R fit
    :param nonzero: nonzero pixels
    :param fits: tuple or fits (left_fit, right_fit) of same order
    :param ord: order of fit being applied
    :return:
    """

    left_fit = fits[0]
    right_fit = fits[1]
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])

    # left_poly_vals = np.zeros(shape=nonzeroy.shape, dtype=np.float64)
    # right_poly_vals = np.zeros(shape=nonzeroy.shape, dtype=np.float64)
    #
    # # Apply the polynomial p(independent_var) to obtain the dependent variable values
    # for i in range(0, ord + 1):
    #     left_poly_vals += left_fit[i]*(np.power(nonzeroy, ord-i))
    #     right_poly_vals += right_fit[i]*(np.power(nonzeroy, ord-i))

    left_poly_vals = poly_eval(left_fit, nonzeroy, ord)
    right_poly_vals = poly_eval(right_fit, nonzeroy, ord)

    # Find the indeces of non-zero pixels around the polynomial - within the margin
    left_lane_inds = (nonzerox > (left_poly_vals - margin)) & (nonzerox < (left_poly_vals + margin))
    right_lane_inds = (nonzerox > (right_poly_vals - margin)) & (nonzerox < (right_poly_vals + margin))

    return left_lane_inds, right_lane_inds

def lanes_viz(img, fits, nonzerox, nonzeroy, lane_inds, margin, ctroffset, ord):

    """
        Visualize lanes onto image. Utility function
    :param img:
    :param fits:
    :param nonzerox:
    :param nonzeroy:
    :param lane_inds:
    :param margin:
    :return:
    Image with visualizations on it
    """

    left_fit, right_fit = fits[0], fits[1]
    left_lane_inds, right_lane_inds = lane_inds[0], lane_inds[1]

    ######## visualizations ######
    out_img = np.dstack((img, img, img)) * 255
    window_img = np.zeros_like(out_img)

    # Generate x and y values for plotting
    ploty = np.linspace(0, out_img.shape[0] - 1, out_img.shape[0])
    left_fitx = poly_eval(left_fit, ploty, ord)
    right_fitx = poly_eval(right_fit, ploty, ord)

    # Paint non-zero pixels, belonging to the line-windows with colors
    out_img[nonzeroy[left_lane_inds], nonzerox[left_lane_inds]] = [255, 0, 0]
    out_img[nonzeroy[right_lane_inds], nonzerox[right_lane_inds]] = [0, 0, 255]

    left_line_window1 = np.array([np.transpose(np.vstack([left_fitx - margin, ploty]))])
    left_line_window2 = np.array([np.flipud(np.transpose(np.vstack([left_fitx + margin, ploty])))])
    left_line_pts = np.hstack((left_line_window1, left_line_window2))
    right_line_window1 = np.array([np.transpose(np.vstack([right_fitx - margin, ploty]))])
    right_line_window2 = np.array([np.flipud(np.transpose(np.vstack([right_fitx + margin, ploty])))])
    right_line_pts = np.hstack((right_line_window1, right_line_window2))

    # Draw the lane onto the blank image
    cv2.fillPoly(window_img, np.int_([left_line_pts]), (0, 255, 0))
    cv2.fillPoly(window_img, np.int_([right_line_pts]), (0, 255, 0))
    out_img = cv2.addWeighted(out_img, 1, window_img, 0.3, 0)

    return out_img

def print_lane_info(img, rad_curv, veh_offset):
    """
    Embed in textual form lane info in image
    :param img: image to embed info into
    :param rad_curv: radius_of_curvature(m)
    :param veh_offset: vehcile offset (m)
    :return: none
    """

    # Print the offset from center lane
    font = cv2.FONT_HERSHEY_SIMPLEX
    # xpos = np.int32(img.shape[1] / 2. - img.shape[1] / 3.)
    xpos = 50
    cv2.putText(img, 'Curv: {0:.2f}km, Ctr. Offset: {1:.2f}m'.format(np.float64(rad_curv/1000.),
                                                                np.float64(veh_offset)),
                (xpos, 100), font, 2, (255, 255, 255), 3)

def lanes(img, initfits, xscale=1, yscale=1, ord=2, viz=True):
    """
    Find pixels in the binary image starting the search from initial fits
    :param img: binary image
    :param initfits: tuple of L/R previous fits to be used as initial starting points (pixel domain)
    :param ord: order of polynomial used for fitting lanes
    :return:
    out_img -  image with rectangles drawn on it (for reference and visualization)
    left_fit, left_fit_scaled, - pixel/scaled fits for left lane
    right_fit, right_fit_scaled - pixel/scale fits for right lane
    nonzerox, nonzeroy - paired arrays for locations of non-zero pixels
    left_lane_inds, right_lane_inds - indeces of pixels belonging to the lanes in the "nonzerox, nonzeroy" pairs
    """
    margin = 100
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])

    left_lane_inds, right_lane_inds = lane_pixels_indeces_around_fit(nonzero, initfits, ord=ord, margin=margin)
    left_fit, left_fit_scaled, right_fit, right_fit_scaled, middle_fit, middle_fit_scaled = \
        compute_fits(img.shape, nonzerox, nonzeroy, left_lane_inds, right_lane_inds, xscale, yscale, ord)

    yrange = np.linspace(0, img.shape[0] - 1, img.shape[0])
    yrange_scaled = yrange * yscale

    # Do the scaled measurements make sense ?
    OK, reason = lanes_ok((left_fit_scaled, right_fit_scaled), var=yrange_scaled)

    ######## visualizations ######
    if viz:
        ctr_offset = center_offset(middle_fit_scaled, np.float64(img.shape[1]) * xscale / 2., np.max(yrange_scaled))
        out_img = lanes_viz(img, fits=(left_fit, right_fit), lane_inds=(left_lane_inds, right_lane_inds),
                             nonzerox=nonzerox, nonzeroy=nonzeroy, margin=margin, ctroffset=ctr_offset, ord=ord)
    else:
        out_img = np.copy(img)

    return out_img, left_fit, left_fit_scaled, \
           right_fit, right_fit_scaled, \
           middle_fit, middle_fit_scaled, OK, reason


#!/usr/bin/python2

import base
import exceptions

import re
import simplejson as json

"""
network: pos-1-8-0-0-cr01.dallas.tx.ibone.comcast.net
                          CITY  .ST

"""

class Comcast(base.DNSGeoBase):
	priority = 80
	applre = re.compile(r".*\.(\w*)\.(\w\w)\.ibone\.comcast\.net$")
	def lookup(self, ip=None, hostname=None):
		if hostname:
			match = Comcast.applre.match(hostname)
			if match:
				city_code, state_code = match.group(1,2)
				returninfo = base.returninfo.copy()
				state = base.get_state_by_code('us', state_code)
				returninfo['country'] = 'United States of America'
				returninfo['state'] = state['name']
				returninfo['accuracy'] = 'state'
				returninfo['lat'] = state['lat']
				returninfo['lng'] = state['lng']
				returninfo['company'] = 'Comcast Corporation'
				returninfo['info'] = 'carrier'
				try:
					jsondb = json.load(open('./database/carriers/comcast.json'))
					c = jsondb[city_code.lower()]
					returninfo['city'] = c['city']
					returninfo['accuracy'] = 'city'
					returninfo['lat'] = c['lat']
					returninfo['lng'] = c['lng']
				except:
					pass
				return returninfo
		raise exceptions.NotApplicable()


from flask import Flask, request
from flask_restful import Resource, Api, reqparse
import time

from sudoku import solve
from utils import values2grid, check_sudoku_input

app = Flask(__name__)
app.config['PROPAGATE_EXCEPTIONS'] = True # To allow flask propagating exception even if debug is set to false on app
api = Api(app)

class Sudoku(Resource):
    parser = reqparse.RequestParser()
    parser.add_argument('sudoku',
        type=str,
        required=True,
        help="This field cannot be left blank!"
    )

    def get(self, sudoku):
        (ok, message) = check_sudoku_input(sudoku)
        if not ok:
            return {'message': message}, 400
        start = time.clock()
        result = solve(sudoku)
        t = time.clock()-start
        return {'solution': values2grid(result), 'time': t}

api.add_resource(Sudoku, '/sudoku/<string:sudoku>')

if __name__ == '__main__':
    app.run(debug=True)  # important to mention debug=True


import os
from math import ceil, floor
from multiprocessing.context import Process

import torch
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms
from torchvision.utils import save_image

from tqdm import tqdm

class ResizeInputDataset(Dataset):
    def __init__(self, root, transform=None, split: float = 1.0):
        self.root = root
        self.transform = transform

        all_items = os.listdir(self.root)
        n = len(all_items)

        lower, upper = split

        self.items = all_items[floor(n*lower):min(ceil(n*upper), n)]

    def __len__(self):
        return len(self.items)

    def __getitem__(self, idx):
        if torch.is_tensor(idx):
            idx = idx.tolist()

        img_name = self.items[idx]
        image = Image.open(os.path.join(self.root, img_name)).convert('RGB')

        if self.transform is not None:
            image = self.transform(image)
        return image, img_name

def compose_transformations(width: int, height: int, center_crop: bool):
    """ Composes a transformation that keeps the size ratio.
    """
    transformations = [
        transforms.Resize(max(width, height), interpolation=Image.BICUBIC),
        transforms.CenterCrop((width, height)),
        transforms.ToTensor()
    ]

    if not center_crop:
        del transformations[1]

    return transforms.Compose(transformations)

def resize_images(input_folder: str, output_folder: str, overwrite: bool, resize_kwargs: dict,
                  split=(0.0, 1.0), index=0):
    ds = ResizeInputDataset(root=input_folder, transform=compose_transformations(**resize_kwargs), split=split)

    for img, img_name in tqdm(ds, desc="Resizing Images", disable=index != 0):
        if not os.path.exists(os.path.join(output_folder, img_name)) or overwrite:
            save_image(img, os.path.join(output_folder, img_name))

def resize_images_parallel(input_folder: str, output_folder: str, overwrite: bool, n_procs: int, resize_kwargs: dict):
    split = 1.0 / n_procs

    processes = []
    for m in range(n_procs):
        this_split = (split * m, split * (m+1))

        p = Process(target=resize_images,
                    args=(input_folder, output_folder, overwrite, resize_kwargs, this_split, m))
        p.daemon = True
        p.start()
        processes.append(p)

    for p in processes:
        p.join()
    print("Done Resizing!")


from six.moves import cPickle as pickle
from random import randint

import numpy as np
import theano as th

class Dataset(object):
    def __init__(self):
        self.train_size = 0
        self.test_size = 0
        self.labels = []
        self.datas = []
        pass
    def load(self, i_=None, is_test_=False, augment_=False):
        if is_test_:
            y_set = self.labels[self.train_size:]
            x_set = self.datas[self.train_size:]
        else:
            y_set = self.labels[:self.train_size]
            x_set = self.datas[:self.train_size]
        if i_ is None:
            i_ = randint(0, len(x_set)-1)
        elif type(i_) is int:
            i_ = [i_]
        if not augment_:
            return x_set[i_], y_set[i_]
        return type(self)._augment_data_batch(x_set[i_]),y_set[i_]

    def _augment_data_batch(v_img_):
        return v_img_

class Cifar10Loader(Dataset):
    def __init__(self):
        self.train_size = 50000
        self.test_size = 10000
        self.labels = np.empty(shape=(60000,), dtype=np.int32)
        self.datas = np.empty(shape=(60000,3,32,32), dtype=np.float32)
        for b in range(5):
            with open('./data/image/cifar10/data_batch_'+str(b+1), 'rb') as f:
                data = pickle.load(f, encoding='bytes')
                self.labels[10000*b:10000*(b+1)] = np.asarray(data[b'labels'], dtype=np.int32)
                self.datas[10000*b:10000*(b+1)]= np.reshape(np.asarray(data[b'data'], dtype=np.float32), (10000,3,32,32))/255.
        with open('./data/image/cifar10/test_batch', 'rb') as f:
            data = pickle.load(f, encoding='bytes')
            self.labels[50000:60000] = np.asarray(data[b'labels'], dtype=np.int32)
            self.datas[50000:60000]= np.reshape(np.asarray(data[b'data'], dtype=np.float32), (10000,3,32,32))/255.

    def _augment_data_batch(v_img_):
        for img in v_img_:
            if randint(0,65535)>32767:
                img[:,:,:] = img[:,:,::-1]
            gamma = np.random.uniform(0.77,1.3, size=(3,1,1))
            img **= gamma
            r,n = divmod(randint(0,31), 8)
            if r==0:
                img[:,:n,:] = 0.
            elif r==1:
                img[:,-n:, :] = 0.
            if r==2:
                img[:,:,:n] = 0.
            elif r==3:
                img[:,:,-n:] = 0.

class MnistLoader(Dataset):
    def __init__(self):
        self.train_size = 60000
        self.test_size = 10000
        self.labels = np.empty(shape=(70000,), dtype=np.int32)
        self.datas = np.empty(shape=(70000,1,28,28), dtype=np.float32)
        with open('./data/image/mnist/mnist-train.pkl', 'rb') as f:
            self.labels[:60000] = np.asarray(pickle.load(f), dtype=np.int32)
            self.datas[:60000] = np.expand_dims(np.asarray([pickle.load(f) for _ in range(60000)], dtype=th.config.floatX), 1)
        with open('./data/image/mnist/mnist-test.pkl', 'rb') as f:
            self.labels[60000:] = np.asarray(pickle.load(f), dtype=np.int32)
            self.datas[60000:] = np.expand_dims(np.asarray([pickle.load(f) for _ in range(10000)], dtype=th.config.floatX), 1)

    def _augment_data_batch(v_img_):
        return v_img_


from Model.Winery import Winery
import pandas as pd
import json

def instantiate_wineries():
    file = pd.read_csv("static/csv/final_scraped_wineries.csv", encoding='latin1')
    parsed_json = file.to_json(orient='records')
    parsed_json = json.loads(parsed_json)
    print(parsed_json)
    new_list = list()
    i = 0
    for item in parsed_json:
        new_list.append(Winery(i,
                               item['Winary Name'],
                               item['Winary Description'],
                               item['Winary Image Link'],
                               item['Winary Rating'],
                               item['Winary Location']))
        i += 1
    return new_list


# -*- coding: utf-8 -*-
import os
import sys
import signal
import argparse

from PyQt5.QtGui import QGuiApplication
from PyQt5.QtCore import QObject, QTimer
from PyQt5.QtQml import QQmlApplicationEngine

from livecoding import start_livecoding_gui

PROJECT_PATH = os.path.dirname(os.path.realpath(__name__))

class MyApp(QObject, object):
    def __init__(self, live, parent=None):
        super(MyApp, self).__init__(parent)

        self._engine = QQmlApplicationEngine()
        self._engine.addImportPath(PROJECT_PATH)
        if live:
            start_livecoding_gui(
                self._engine, PROJECT_PATH, __file__, live_qml='./live.qml'
            )  # live_qml is optional and can be used to customize the live coding environment
        else:
            qml_main = os.path.join(PROJECT_PATH, 'main.qml')
            self._engine.load(qml_main)

        self._start_check_timer()

    def _start_check_timer(self):
        self._timer = QTimer()
        self._timer.timeout.connect(lambda: None)
        self._timer.start(100)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, lambda *args: QGuiApplication.quit())

    parser = argparse.ArgumentParser(
        description="""
            Example App
            """
    )
    parser.add_argument(
        '-l',
        '--live',
        help='The live coding version of this application',
        action='store_true',
    )
    args = parser.parse_args()

    app = QGuiApplication(sys.argv)

    gui = MyApp(live=args.live)

    sys.exit(app.exec_())


# -*- coding: utf-8 -*-
"""
Python script to test AL on binary activity coefficient data by computing
VLE phase diagrams.

Sections:
    . Imports
    . Configuration
    . Ground Truth VLE
    . Active Learning VLE
    . Publication Plots

Last edit: 2022-10-27
Author: Dinis Abranches
"""

# =============================================================================
# Imports
# =============================================================================

# Specific
import numpy
from matplotlib import pyplot as plt

# Local
from lib import thermoAux as thermo
from lib import mlAux as ml

# =============================================================================
# Configuration
# =============================================================================

# System definition
components=['Acetone','Cyclohexane']

# GP Configuration
gpConfig={'kernel':'RQ',
          'useWhiteKernel':False,
          'doLogY':True,
          'indepDim':False,
          'trainLikelihood':False
          }

# VLE Pressure
P=101325

# =============================================================================
# Ground Truth VLE
# =============================================================================

# Get NRTL parameters
parameters=thermo.NRTL_Parameters(components[0],components[1])
# Define gamma functions
def F_Gamma_1_NRTL(x1,T):
    gammas=thermo.NRTL_Binary_getGamma(parameters,x1,T)
    return gammas[0]
def F_Gamma_2_NRTL(x1,T):
    gammas=thermo.NRTL_Binary_getGamma(parameters,x1,T)
    return gammas[1]
Fs_gamma_NRTL=[F_Gamma_1_NRTL,F_Gamma_2_NRTL]
# Get Antoine parameters
antoine1=thermo.antoineParameters(components[0])
antoine2=thermo.antoineParameters(components[1])
# Define Vapor Pressure Functions
def F_VP_1(T):
    P=thermo.antoineEquation(antoine1,T,getVar='P')
    return P
def F_VP_2(T):
    P=thermo.antoineEquation(antoine2,T,getVar='P')
    return P
def F_Inverse_VP_1(P):
    T=thermo.antoineEquation(antoine1,P,getVar='T')
    return T
def F_Inverse_VP_2(P):
    T=thermo.antoineEquation(antoine2,P,getVar='T')
    return T
Fs_VP=[F_VP_1,F_VP_2]
Fs_Inverse_VP=[F_Inverse_VP_1,F_Inverse_VP_2]
# Compute VLE
bubble_NRTL,dew_NRTL,gammas_NRTL=thermo.compute_Tx_VLE_Binary(Fs_gamma_NRTL,
                                              Fs_VP,
                                              Fs_Inverse_VP,
                                              P,
                                              z1_range=numpy.linspace(0,1,101),
                                              do_Bubble_Only=True)

# =============================================================================
# Active Learning VLE
# =============================================================================

# Testing Set Grid
x1_range=numpy.linspace(0,1,101)
# Perform AL
bubble_gp,dew_gp,gamma_gp,MAF_Vector,X_AL \
    =ml.AL_VLE_Binary_Type1(F_Gamma_1_NRTL,F_Gamma_2_NRTL,gpConfig,P,Fs_VP,
                            Fs_Inverse_VP,x1_range,maxIter=100,min_AF=0.01,
                            bubbleTruth=bubble_NRTL,dewTruth=dew_NRTL,
                            plot_VLE_GIF=r'C:\Users\dinis\Desktop\testes\vle.gif',
                            title=None)

# =============================================================================
# Pulbication Plots
# =============================================================================

# Plot Configuration
plt.rcParams['figure.dpi'] = 600
plt.rcParams['text.usetex'] = False
plt.rcParams['font.family'] = 'serif'
plt.rcParams['font.serif'] = 'Times New Roman'
plt.rcParams['font.weight'] = 'bold'
plt.rcParams['font.size'] = 8
plt.rcParams['figure.titlesize'] = 8
plt.rcParams['figure.titleweight'] = 'bold'
plt.rcParams['axes.titlesize'] = 8
plt.rcParams['axes.titleweight'] = 'bold'
plt.rcParams['axes.labelsize'] = 8
plt.rcParams['axes.labelweight'] = 'bold'
plt.rcParams['xtick.labelsize'] = 7
plt.rcParams['ytick.labelsize'] = 7
plt.rcParams['mathtext.rm'] = 'serif'
plt.rcParams['mathtext.it'] = 'serif:italic'
plt.rcParams['mathtext.bf'] = 'serif:bold'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams["savefig.pad_inches"] = 0.02
plt.rcParams['savefig.dpi'] = 600
# Create figure
plt.figure(figsize=(3,1.7))
# Plots
plt.plot(bubble_NRTL[:,0],bubble_NRTL[:,1],'-k',linewidth=1,
         label='Ground Truth')
plt.plot(dew_NRTL[:,0],dew_NRTL[:,1],'-k',linewidth=1)
plt.plot(bubble_gp[:,0],bubble_gp[:,1],'--r',linewidth=1,
         label='GP-Predicted')
plt.plot(dew_gp[:,0],dew_gp[:,1],'--r',linewidth=1)
plt.plot(X_AL[:,0],X_AL[:,1],'ob',markersize=2,label='AL Training Data')
plt.xlabel('$\mathregular{z_{'+components[0]+'}}$')
plt.ylabel('T /K',fontsize = 7)
# plt.text(0.18,0.05,'VLE (P = '+str(int(P/101325))+' atm)',
#          color='black',
#          horizontalalignment='center',
#          verticalalignment='center',
#          transform=plt.gca().transAxes)
plt.text(0.02,0.05,components[0]+'/'+components[1],
         color='black',
         horizontalalignment='left',
         verticalalignment='center',
         transform=plt.gca().transAxes)
plt.legend(prop={'size': 6})