import collections

from django.core.paginator import Paginator
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import QuoteForm, AuthorForm, DeleteAuthorForm, TagForm, DeleteTagForm
from .models import Quote, Author, Tag
# Create your views here.

def get_top_10_tags():
    quotes = Quote.objects.all()
    all_tags = []
    for quote in quotes:
        for tag in quote.tags.all():
            all_tags.append(tag.tag)
    tags_count = collections.Counter(all_tags)
    most_tags = tags_count.most_common(10)
    top_10_tags = []
    for tag in most_tags:
        top_10_tags.append(tag[0])
    return top_10_tags

def main(request, page=1):
    quotes = Quote.objects.all()
    per_page = 10
    paginator = Paginator(list(quotes), per_page)
    quotes_page = paginator.page(page)
    top_10_tags = get_top_10_tags()
    return render(request, 'quotes/index.html', context={'title': 'Quotes to Scrape', 'quotes': quotes_page,
                                                         'top_10_tags': top_10_tags, 'current_page': page})

def get_author(request, author):
    author_fullname = author.split('-')[-1]
    author = Author.objects.filter(fullname__contains=author_fullname)[0]
    top_10_tags = get_top_10_tags()
    return render(request, 'quotes/get_author.html', context={'title': 'Quotes to Scrape', 'author': author,
                                                              'top_10_tags': top_10_tags})

@login_required
def add_quote(request):
    if request.method == "POST":
        form = QuoteForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("/")
    else:
        quote_form = QuoteForm()

    return render(request, "quotes/add_quote.html", {"quote_form": quote_form})

@login_required
def add_author(request):
    if request.method == "POST":
        form = AuthorForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("/")
    else:
        author_form = AuthorForm()

    return render(request, "quotes/add_author.html", {"author_form": author_form})

@login_required
def add_tag(request):
    if request.method == "POST":
        form = TagForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("/")
    else:
        tag_form = TagForm()

    return render(request, "quotes/add_tag.html", {"tag_form": tag_form})

@login_required
def delete_quote(request, quote_id):
    quote = Quote.objects.get(id=quote_id)
    quote.delete()
    return redirect(to='quotes:root')

@login_required
def delete_author(request):
    del_author_form = DeleteAuthorForm()
    if request.method == "POST":
        form = DeleteAuthorForm(request.POST)
        if form.is_valid():
            author = Author.objects.filter(fullname=form.cleaned_data['fullname'])[0]
            author.delete()
            return redirect(to="quotes:root")

    return render(request, "quotes/delete_author.html", {"del_author_form": del_author_form})

@login_required
def delete_tag(request):
    delete_tag_form = TagForm()
    if request.method == "POST":
        form = TagForm(request.POST)
        if form.is_valid():
            tag = Tag.objects.filter(tag=form.cleaned_data['tag'])[0]
            tag.delete()
            return redirect(to="quotes:root")

    return render(request, "quotes/delete_tag.html", {"delete_tag_form": delete_tag_form})

def get_tag(request, tag, page=1):
    tag_ = Tag.objects.get(tag=tag)
    quotes = Quote.objects.filter(tags=tag_.id)
    per_page = 10
    paginator = Paginator(list(quotes), per_page)
    quotes_page = paginator.page(page)
    tag_url = f'tag/{tag}/page'
    top_10_tags = get_top_10_tags()
    return render(request, 'quotes/index.html', context={'title': 'Quotes to Scrape', 'quotes': quotes_page,
                                                         'tag_url': tag_url, 'current_page': page, 'top_10_tags': top_10_tags})


import pytest

import ezdxf
from ezdxf.entities.underlay import PdfDefinition, PdfUnderlay

@pytest.fixture(scope="module")
def doc():
    return ezdxf.new("R2000")

@pytest.fixture
def pdf_def(doc):
    return PdfDefinition.from_text(PDF_DEFINITION, doc)

def test_pdf_def_properties(pdf_def):
    assert "PDFDEFINITION" == pdf_def.dxftype()
    assert "PDFUNDERLAY" == pdf_def.entity_name

def test_pdf_def_dxf_attribs(pdf_def):
    assert "underlay.pdf" == pdf_def.dxf.filename
    assert "underlay_key" == pdf_def.dxf.name

@pytest.fixture
def pdf(doc):
    return PdfUnderlay.from_text(PDF_UNDERLAY, doc)

def test_pdf_properties(pdf):
    assert "PDFUNDERLAY" == pdf.dxftype()

def test_pdf_dxf_attribs(pdf):
    assert pdf.dxf.insert == (0.0, 0.0, 0.0)
    assert pdf.dxf.scale_x == 2.5
    assert pdf.dxf.scale_y == 2.5
    assert pdf.dxf.scale_z == 2.5
    assert pdf.scaling == (2.5, 2.5, 2.5)
    assert pdf.dxf.flags == 2
    assert pdf.clipping == 0
    assert pdf.on == 1
    assert pdf.monochrome == 0
    assert pdf.adjust_for_background == 0
    assert pdf.dxf.contrast == 100
    assert pdf.dxf.fade == 0
    assert pdf.dxf.underlay_def_handle == "DEAD1"

def test_get_boundary_path(pdf):
    assert pdf.boundary_path == []

def test_reset_boundary_path(pdf):
    pdf.reset_boundary_path()
    assert pdf.boundary_path == []
    assert pdf.clipping is False

def test_set_boundary_path(pdf):
    pdf.set_boundary_path([(0, 0), (640, 180), (320, 360)])  # 3 vertices triangle
    assert pdf.clipping == 1
    assert pdf.boundary_path == [(0, 0), (640, 180), (320, 360)]

def test_set_scale(pdf):
    pdf.scaling = (1.2, 1.3, 1.4)
    assert pdf.scaling == (1.2, 1.3, 1.4)

    pdf.scaling = 1.7
    assert pdf.scaling == (1.7, 1.7, 1.7)

@pytest.fixture
def new_doc():
    # setting up a drawing is expensive - use as few test methods as possible
    return ezdxf.new("R2000")

def test_new_pdf_underlay_def(new_doc):
    rootdict = new_doc.rootdict
    assert "ACAD_PDFDEFINITIONS" not in rootdict
    underlay_def = new_doc.add_underlay_def("underlay.pdf", fmt="pdf", name="u1")

    # check internals pdf_def_owner -> ACAD_PDFDEFINITIONS
    pdf_dict = rootdict["ACAD_PDFDEFINITIONS"]
    assert underlay_def.dxf.owner == pdf_dict.dxf.handle

    assert "underlay.pdf" == underlay_def.dxf.filename
    assert "u1" == underlay_def.dxf.name

def test_new_pdf(new_doc):
    msp = new_doc.modelspace()
    underlay_def = new_doc.add_underlay_def("underlay.pdf")
    underlay = msp.add_underlay(underlay_def, insert=(0, 0, 0), scale=2)
    assert underlay.dxf.insert == (0, 0, 0)
    assert underlay.dxf.scale_x == 2
    assert underlay.dxf.scale_y == 2
    assert underlay.dxf.scale_z == 2
    assert underlay_def.dxf.handle == underlay.dxf.underlay_def_handle
    assert underlay.clipping is False
    assert underlay.on is True
    assert underlay.monochrome is False
    assert underlay.adjust_for_background is True
    assert underlay.dxf.flags == 10

    underlay_def2 = underlay.get_underlay_def()
    assert underlay_def.dxf.handle == underlay_def2.dxf.handle

class TestCopyAndTransformUnderlay:
    @pytest.fixture(scope="class")
    def doc(self):
        return ezdxf.new()

    @pytest.fixture(scope="class")
    def underlay_def(self, doc):
        return doc.add_underlay_def("underlay.pdf")

    @pytest.fixture(scope="class")
    def msp(self, doc):
        return doc.modelspace()

    def test_copied_underlay_has_same_underlay_definition(self, msp, underlay_def):
        underlay = msp.add_underlay(underlay_def, insert=(0, 0, 0), scale=2)
        clone = underlay.copy()
        msp.add_entity(clone)
        assert (
            underlay.get_underlay_def() is clone.get_underlay_def()
        ), "expected the same underlay definition"

    def test_underlay_definition_has_reactor_handles_to_copies(self, msp, underlay_def):
        underlay = msp.add_underlay(underlay_def, insert=(0, 0, 0), scale=2)
        clone = underlay.copy()
        msp.add_entity(clone)

        assert (
            underlay.dxf.handle in underlay_def.reactors
        ), "expected reactor handle of original underlay"
        assert (
            clone.dxf.handle in underlay_def.reactors
        ), "expected reactor handle of cloned underlay"

    def test_transform_underlay(self, msp, underlay_def):
        """The UNDERLAY entity uses the same low-level transform function as INSERT,
        no extensive transformation testing is required.
        """
        underlay = msp.add_underlay(underlay_def, insert=(0, 0, 0), scale=2)
        underlay.translate(1, 2, 3)

        assert underlay.dxf.insert == (1, 2, 3)

PDF_DEFINITION = """  0
PDFDEFINITION
  5
DEAD1
330
DEAD2
100
AcDbUnderlayDefinition
  1
underlay.pdf
  2
underlay_key
"""

PDF_UNDERLAY = """  0
PDFUNDERLAY
  5
DEAD3
330
DEAD4
100
AcDbEntity
  8
0
100
AcDbUnderlayReference
340
DEAD1
 10
0.0
 20
0.0
 30
0.0
 41
2.5
 42
2.5
 43
2.5
280
  2
281
   100
282
     0
"""


def sum_2_num(num1,num2):
    """对两个数字求和"""
    result = num1 + num2
    # 使用返回值告诉调用方结果
    return result
    #return下面的代码不会被执行

# 可以使用变量来接受函数返回结果
a = sum_2_num(10,30)
print("结果是%d"%a)


from collections import deque

n,m = map(int,input().split())
graph =[]
queue = deque()
answer = 0
dx = [0,1]
dy=  [1,0]
for _ in range(n):
    graph.append(list(map(int, input())))

def bfs(x,y):
    global answer
    graph.append((x,y))
    while queue:
        v = queue.popleft()
        if graph[v[0]][v[1]] == 0:
            return
        if graph[v[0]][v[1]] == 1:
            answer += 1
        for i in range(2):
            X = dx[i] + v[0]
            Y = dy[i] + v[1]
            queue.append((X,Y))
    return answer

print(bfs(0,0))


import pandas as pd
import json
import datetime

def flatten_dict(data, parent_key='', sep='_'):
    flattened_dict = {}
    for key, value in data.items():
        new_key = parent_key + sep + key if parent_key else key
        if isinstance(value, dict):
            flattened_dict.update(flatten_dict(value, new_key, sep))
        elif isinstance(value, list):
            for i, item in enumerate(value):
                new_key = new_key + sep + str(i)
                if isinstance(item, dict):
                    flattened_dict.update(flatten_dict(item, new_key, sep))
                else:
                    flattened_dict[new_key] = item
        else:
            flattened_dict[new_key] = value
    return flattened_dict

def extract_scans_from_file(file_path):
    with open(file_path, 'r') as file:
        json_response = file.read()

    response_data = json.loads(json_response)
    scans = response_data.get("scans", [])

    return scans

# Example usage:
#json_file_path = "/Users/suryap/git/pythonDev/jsonResponse.json"
json_file_path = "/Users/suryap/git/pythonDev/example.json"
scans = extract_scans_from_file(json_file_path)

# Flatten nested dictionaries and lists
normalized_scans = []
for scan in scans:
    flattened_scan = flatten_dict(scan)
    normalized_scans.append(flattened_scan)

# Create DataFrame from normalized scans
df = pd.DataFrame(normalized_scans)

# Get the current date
current_date = datetime.datetime.now().strftime("%B-%d-%Y")

# Specify the output CSV file path
print("PRINTING CSV........")
# Generate the modified file name with the current date
modified_file_name = f"CxOne_Scans_Data_{current_date}.csv"

# Specify the output CSV file path with the modified file name
csv_file_path = f"/Users/suryap/git/pythonDev/{modified_file_name}"

# Export DataFrame to CSV file
df.to_csv(csv_file_path, index=False)


import sys
from cx_Freeze import setup, Executable

build_exe_options = {"packages": ["os"], "includes": ["tkinter", "pandas", "sqlite3", "openpyxl", "datetime", "tkcalendar", "sys"]}

base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(
    name="Meu App",
    version="0.1",
    description="Minha 1° Aplicação!",
    options={"build_exe": build_exe_options},
    executables=[Executable("main.py", base=base)]
)


'''
Dictionary - They are key-Value pairs, Unordered, Mutable.
Created with braces '{}' and separet items with ':'
'''

mydict = {
    "name": "Susan",
    "age": 23,
    "city": "New York"
}
print(mydict)

mydict2 = dict(name="Laylah", age=2, city="Boston")
print(mydict2)

del mydict2["age"]
print(mydict2)


import typing
import numpy as np
import onnx
import ir.framework

def onnx_tensor_to_symbol(tensor: onnx.TensorProto, name: str = None):
    def transfer_tensor_data(tensor: onnx.TensorProto, dtype):
        if tensor.float_data:
            return np.asarray(list(tensor.float_data), dtype=dtype)
        elif tensor.int32_data:
            return np.asarray(list(tensor.int32_data), dtype=dtype)
        elif tensor.string_data:
            return np.asarray(tensor.string_data, dtype=dtype)
        elif tensor.int64_data:
            return np.asarray(list(tensor.int64_data), dtype=dtype)
        elif not tensor.raw_data:
            return np.asarray([], dtype=dtype)
        else:
            return np.frombuffer(tensor.raw_data, dtype=dtype)

    if tensor.data_type == onnx.TensorProto.FLOAT:
        data = transfer_tensor_data(tensor, np.float32)
    elif tensor.data_type == onnx.TensorProto.DOUBLE:
        data = transfer_tensor_data(tensor, np.float64)
    elif tensor.data_type == onnx.TensorProto.UINT8:
        data = transfer_tensor_data(tensor, np.uint8)
    elif tensor.data_type == onnx.TensorProto.INT8:
        data = transfer_tensor_data(tensor, np.int8)
    elif tensor.data_type == onnx.TensorProto.UINT16:
        data = transfer_tensor_data(tensor, np.uint16)
    elif tensor.data_type == onnx.TensorProto.INT16:
        data = transfer_tensor_data(tensor, np.int16)
    elif tensor.data_type == onnx.TensorProto.UINT32:
        data = transfer_tensor_data(tensor, np.uint32)
    elif tensor.data_type == onnx.TensorProto.INT32:
        data = transfer_tensor_data(tensor, np.int32)
    elif tensor.data_type == onnx.TensorProto.UINT64:
        data = transfer_tensor_data(tensor, np.uint64)
    elif tensor.data_type == onnx.TensorProto.INT64:
        data = transfer_tensor_data(tensor, np.int64)
    elif tensor.data_type == onnx.TensorProto.BOOL:
        data = transfer_tensor_data(tensor, np.bool)
    elif tensor.data_type == onnx.TensorProto.STRING:
        data = transfer_tensor_data(tensor, np.str_)
    else:
        raise RuntimeError("unsupport data type: {}".format(tensor.data_type))
    if tensor.dims:
        shape = list(tensor.dims)
        data = data.reshape(shape)
    else:
        shape = [1]

    dtype = ir.framework.DataType(tensor.data_type)

    if not name:
        name = tensor.name

    assert name

    symbol = ir.framework.Symbol(name)
    symbol.dtype = dtype
    if tensor.dims:
        symbol.stype = ir.framework.SymbolType.CONSTANT_TENSOR
    elif data.size == 1:
        symbol.stype = ir.framework.SymbolType.CONSTANT_VALUE
    else:
        symbol.stype = ir.framework.SymbolType.CONSTANT_VECTOR
    symbol.shape = shape
    symbol.data = data
    symbol.origin_stype = symbol.stype

    return symbol

def onnx_value_info_to_symbol(tensor: onnx.ValueInfoProto):
    assert tensor.type.tensor_type
    shape = []
    for s in tensor.type.tensor_type.shape.dim:
        if s.dim_param:
            shape.append(s.dim_param)
        elif s.dim_value:
            shape.append(s.dim_value)
        else:
            raise RuntimeError("Unknow shape format")
    symbol = ir.framework.symbol.Symbol(tensor.name)
    symbol.dtype = tensor.type.tensor_type.elem_type
    symbol.shape = shape
    return symbol

def parse_onnx_attribute(attr: onnx.AttributeProto):
    name = attr.name
    value = None
    if attr.type == onnx.AttributeProto.FLOAT:
        value = attr.f
    elif attr.type == onnx.AttributeProto.INT:
        value = attr.i
    elif attr.type == onnx.AttributeProto.STRING:
        value = attr.s
    elif attr.type == onnx.AttributeProto.TENSOR:
        value = attr.t
    elif attr.type == onnx.AttributeProto.GRAPH:
        value = attr.g
    elif attr.type == onnx.AttributeProto.SPARSE_TENSOR:
        value = attr.sparse_tensor
    elif attr.type == onnx.AttributeProto.FLOATS:
        value = list(attr.floats)
    elif attr.type == onnx.AttributeProto.INTS:
        value = list(attr.ints)
    elif attr.type == onnx.AttributeProto.STRINGS:
        value = list(attr.strings)
    elif attr.type == onnx.AttributeProto.TENSORS:
        value = list(attr.tensors)
    elif attr.type == onnx.AttributeProto.GRAPHS:
        value = list(attr.graphs)
    elif attr.type == onnx.AttributeProto.SPARSE_TENSORS:
        value = list(attr.sparse_tensors)
    return name, value


import streamlit as st
from streamlit_option_menu import option_menu
import requests
from streamlit_lottie import st_lottie
from PIL import Image
st.set_page_config(page_title="My WebPage",page_icon=":tada:",layout="wide")

def load_lottieurl(url):
    r = requests.get(url)
    if r.status_code != 200:
        return None
    return r.json()

lottie_coder = load_lottieurl("https://assets10.lottiefiles.com/packages/lf20_7VEmrT02fx.json")
lottie_content = load_lottieurl("https://assets4.lottiefiles.com/packages/lf20_lt8ter7g.json")
image = Image.open('content/an-electroencephalogram-or-eeg-being-performed-image-credit-baburov-2009.jpg')

def local_csv(file_name):
    with open(file_name) as f:
        st.markdown(f"<style>(f.read())</style>",unsafe_allow_html=True)
local_csv("content/style.css")

def app():
    st.write("##")
    st.subheader("Hey Guys :wave:")
    st.title("My Portfolio Website ")
    st.write("""
        I am gonna explain briefly about how streamlit can be used to build interactive and responsibe website to deploy any Machine learning model.
    """)
    st.write("[Read More](https://streamlit.io/)")
    st.write("---")

    with st.container():
        selected = option_menu(
            menu_title = None,
            options = ['About','Projects','Contacts'],
            icons = ['person','code-slash','chat-left-text-fill'],
            orientation = 'horizontal'
        )
    if selected =='About':
        with st.container():
            col1, col2 = st.columns(2)
            with col1:
                st.write("##")
                st.subheader("I am Rahul Jain")
                st.title("Undergrad at USAR")
            with col2:
                st_lottie(lottie_coder)
            st.write("---")

            with st.container():
                col3,col4= st.columns(2)
                with col3:
                    st.subheader("""
                    Education
                    - GGSIPU
                        - Bachelor of Engineering - Artifical Intelligence and Data Science
                        - CGPA - 9.5 (overall)
                    - Preet Public Sr Sec School
                        - PCM
                        - Percentage - 90%

                    """)
                with col4:
                    st.subheader("""
                    Experience
                    - GirlScript Summer of Code
                        -  Duration - 3 months
                        - Remote
                    """)

    if selected == 'Projects':
        with st.container():
            st.header("My Projects ")
            st.write("##")
            col5,col6 = st.columns((1,2))
            with col5 :
                st.image(image)
            with col6 :
                st.subheader("EEG Eye State Classification")
                st.write("""
                EEG eye state classification begins with the collection of EEG data using specialized electrodes placed on the scalp to measure the electrical activity in the brain. Simultaneously, eye state labels are recorded, typically through manual observation or eye-tracking devices. The collected EEG data undergoes preprocessing steps to remove noise, artifacts, and baseline drift, ensuring the data is clean and suitable for analysis. EEG Eye State Classification refers to the task of analyzing electroencephalogram (EEG) data collected from the human brain to determine the current state of the eyes, whether they are open or closed
                """)
                st.markdown("[Visit Github Repository](https://github.com/Rahul-Jain-1/ML-project-Eye-State-Classification)")

    if selected == 'Contacts':
        st.header("Get In Touch ")
        st.write("##")
        st.write("##")
        contact_form = """"
        <div class="form-container">
            <form action="https://formsubmit.co/your@email.com" method="POST">
            <input type="text" name="name" placeholder="Name" required>
            <input type="email" name="email" placeholder="Email" required>
            <input type="text" name="subject" placeholder="Subject" required>
            <textarea name="message" placeholder="Message" rows="5" required></textarea>
            <button type="submit">Send</button>
            </form>
        </div>
        """
        left_col, right_col = st.columns((2,1))
        with left_col:
            st.markdown(contact_form, unsafe_allow_html=True)
        with right_col:
            st_lottie(lottie_content,height=300)
    # #load assets
    # lottie_coding = "https://assets1.lottiefiles.com/packages/lf20_0yfsb3a1.json"

    # #header
    # with st.container():
    #     st.subheader("Hi I am Rahul Jain")
    #     st.title("A student from GGSIPU")
    #     st.write("I am passoiniate about working with real time ml models. ")
    #     st.write("[Learn More?](https://www.kaggle.com/search?q=face+and+eye)")

    # with st.container():
    #     st.write("---")
    #     left_column, right_column = st.columns(2)
    #     with left_column:
    #         st.header("What i do ")
    #         st.write("##")
    #         st.write(
    #             """

    #             """
    #         )
    #         st.write("[Utine channel](https://www.youtube.com/watch?v=VqgUkExPvLY)")

    #     with right_column:
    #         st_lottie(lottie_coding,height=300,key='coding ')

    #     # ---- PROJECTS ----
    # with st.container():
    #     st.write("---")
    #     st.header("My Projects")
    #     st.write("##")
    #     image_column, text_column = st.columns((1, 2))
    #     # with image_column:
    #     #     st.image(img_lottie_animation)
    #     with text_column:
    #         st.subheader("Integrate Lottie Animations Inside Your Streamlit App")
    #         st.write(
    #             """
    #             Learn how to use Lottie Files in Streamlit!
    #             Animations make our web app more engaging and fun, and Lottie Files are the easiest way to do it!
    #             In this tutorial, I'll show you exactly how to do it
    #             """
    #         )

    #         st.markdown("[Watch Video...](https://youtu.be/TXSOitGoINE)")

    # with st.container():
    #     image_column, text_column = st.columns((1, 2))
    #     # with image_column:
    #     #     st.image(img_contact_form)
    #     with text_column:
    #         st.subheader("How To Add A Contact Form To Your Streamlit App")
    #         st.write(
    #             """
    #             Want to add a contact form to your Streamlit website?
    #             In this video, I'm going to show you how to implement a contact form in your Streamlit app using the free service ‘Form Submit’.
    #             """
    #         )
    #         st.markdown("[Watch Video...](https://youtu.be/FOULV9Xij_8)")

if __name__ == "__main__":
    app()


from threading import Thread
from haversine import haversine
import urllib3
import json
import pandas as pd
import numpy as np
import datetime
import time
import pytz
from math import floor, ceil
from statistics import mean

# Attempt 1: Simply calculating the distance of the moving car to the destination
# Attempt 2:
## Using the segmented 'route', find the segment where the moving car is and where the destination is.
## Calculate the distance of the moving car from the destination 'indirectly' by summing up the distances of the segments in between the moving car and the destination

def calculateDistanceOfSegments(route, start_segment, end_segment):
    total_distance = 0.0
    start = start_segment
    end = end_segment + 1
    # if end > len(route):
    #     end = 0
    while start != end:
        if(start > len(route)-1):     # resets the counter
            start = 0
        # print(route[start]["segment_id"])
        total_distance = total_distance + route[start]["length"]
        start+=1
    return total_distance

# load the segmentroute JSON file
with open("route_test.json", "r") as read_file:
    route_data = json.load(read_file)

test = calculateDistanceOfSegments(route_data, 0, 41)
print(test)

# 'dripping' part
csv_data = open("datasets/drive.csv", "r")
lines = csv_data.readlines()
read  = False
location = []

for k in lines:
    if read == False:
        read = True
    else:
        j = k.split(",")
        unx = (datetime.datetime.fromtimestamp(int(j[0]) // 1000) - datetime.datetime(1970,1,1)).total_seconds()
        to_append = (unx, float(j[1]), float(j[2]), float(j[3])) # time, latitude, longitude, speed
        location.append(to_append)

for k in range(0,len(location)):
    if k < len(location)-1:
        delay = float(location[k+1][0])-float(location[k][0])
        if delay <= 0:
            del location[k+1]

# random destination target
destination = (14.65216416,121.06229386)

# calculate the distance of the destination point to the midpoint of all segments
# 'snap' the point to the segment with the minimum distance wrt to it.
segment_distances = []
for segment in route_data:
    distance = haversine(destination, segment["midpoint"])
    segment_distances.append(distance)

destination_segment = segment_distances.index(min(segment_distances))

car_snapped = False
distance_from_dest = 9999

k = 0

speed = []

while distance_from_dest > 0:
    u = datetime.datetime.utcnow()
    u = u.replace(tzinfo=pytz.utc)
    if location[k][3]!="":
        current_coordinates = (location[k][1], location[k][2])

        # determine which segment is the car nearest to
        segment_distances = []
        for segment in route_data:
            distance = haversine(current_coordinates, segment["midpoint"])*1000
            segment_distances.append(distance)

        car_segment = segment_distances.index(min(segment_distances))
        print('min distance from segment [%d]' %(car_segment) ,min(segment_distances))

        # calculate the total distance from the car's current segment to the destination's segment
        distance_from_dest = calculateDistanceOfSegments(route_data, car_segment, destination_segment)*1000
        # distance_from_dest = haversine(current_coordinates, destination)*1000.0 # this is in KM

        # calculate for the average speed by getting the mean of all the speed a car has sustained so far
        current_speed = location[k][3]
        speed.append(current_speed)
        ave_speed = mean(speed)

        eta = ceil((distance_from_dest/ave_speed)/60.0)
        print(distance_from_dest, " m left", "will arrive in ", eta, " mins")
    if k < len(location)-1:
        delay = float(location[k+1][0])-float(location[k][0])
        # time.sleep(delay/4.0)
    k+=1

print("destination_segment: ", destination_segment)


class Solution:
    def removeDuplicates(self, nums):
        if nums is None:
            return 0
        cnt = 0
        pre = None
        for numIndex in range(len(nums)):
            if nums[numIndex] == pre:
                nums[numIndex] = None
                cnt += 1
            else:
                pre = nums[numIndex]
        res = len(nums) - cnt
        for cntIndex in range(cnt):
            nums.remove(None)
        return res


# python train.py -d data.json

import torch
import torch.nn as nn
from torch import optim
from torch.utils import data

import torchvision
from torchvision import transforms

import os
import cv2
import time
import argparse
import numpy as np
from tqdm import tqdm
from PIL import Image
import matplotlib.pyplot as plt

from model.unet import UNet
from model.pspnet import PSPNET
from modules.dataloader import Dataset
from modules.loss import BCEFocalLoss, TverskyLoss, FocalTverskyLoss

from modules.metrics import IoULoss

torch.cuda.empty_cache()

def save_loss_image(train_loss, val_loss, epoch, PATH):

	fig = plt.figure()
	plt.plot([k for k in range(1, epoch + 1)], train_loss, label = "Training Loss")
	plt.plot([k for k in range(1, epoch + 1)], val_loss, label = "Validation Loss")
	plt.legend()
	plt.title(os.path.split(PATH)[-1])
	fig.canvas.draw()
	img = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
	img  = img.reshape(fig.canvas.get_width_height()[::-1] + (3,))
	img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
	cv2.imwrite(f"{PATH}_loss.jpg", img)

def train(args):

	if not os.path.isdir(args['save_dir']):
		os.mkdir(args['save_dir'])

	args['size'] = int(args['size'])
	device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

	PATH = os.path.join(args['save_dir'], f"{args['model']}_{args['loss']}")

	if args['model'] == 'pspnet':
		model = PSPNET(output_size=int(args['size']), num_classes=int(args['classes']))

	elif args['model'] == 'unet':
		channels = [3, 8, 16, 32, 64, 128, 256, 512]
		model = UNet(channels, outputSize=int(args['size']), num_classes=int(args['classes']))
	else:
		raise TypeError("Enter Valid Model Name")
	if os.path.isfile(PATH+".pth"):
		answer = input("Model is already exist would you like to load the weights (y/n): ")
		if (answer.lower() == "y") or (answer.lower() == "yes"):
			model.load_state_dict(torch.load(PATH+".pth"))
	print(model)

	model.to(device)

	# '''
	if args['loss'] == 'binary':
		criterion = nn.BCELoss()
	elif args['loss'] == 'focal':
		criterion = BCEFocalLoss(0.8, 2)
	elif args['loss'] == 'tversky':
		criterion = TverskyLoss()
	elif args['loss'] == 'focaltversky':
		criterion = FocalTverskyLoss()
	else:
		raise TypeError("Enter Valid Loss Name")

	metric = IoULoss()

	optimizer = optim.Adam(model.parameters(), lr=args['lr'], weight_decay=1e-3)

	oneHot = True
	if int(args['classes']) == 2:
		oneHot = False

	trainDataset = Dataset(imageDir=args['train_images'], maskDir=args['train_masks'],
		imageSize=args['size'], oneHot=oneHot, numClasses=int(args['classes']), aug=True)

	dataloader = data.DataLoader(trainDataset, batch_size=args['batch_size'], shuffle=True)

	if args['validation']:
		testDataset = Dataset(imageDir=args['val_images'], maskDir=args['val_masks'],
			imageSize=int(args['size']), oneHot=oneHot, numClasses=int(args['classes']), aug=False)

		testDataLoader = data.DataLoader(testDataset, batch_size=args['batch_size'], shuffle=True)

	minValLoss = None
	trainEpochLoss = []
	valEpochLoss = []

	for epoch in range(1, int(args['epochs']) + 1):

		torch.cuda.empty_cache()

		model.train()
		with tqdm(dataloader, unit="batch", leave=False) as tepoch:
			trainLoss = []
			torch.cuda.empty_cache()
			for i, inputs in enumerate(tepoch):

				tepoch.set_description(f"Training Epoch {epoch}")
				# torch.cuda.empty_cache()

				optimizer.zero_grad()

				output = model.forward(inputs[0].to(device))

				loss = criterion(output, inputs[1].to(device))

				loss.backward()
				optimizer.step()

				# accuracy = metric(output, inputs[1].to(device)).item()

				loss_value = loss.item()
				trainLoss.append(loss_value)

				tepoch.set_postfix(loss=loss_value)

		# Validation
		valLoss = []
		model.eval()
		if args['validation']:
			with torch.no_grad():
				with tqdm(testDataLoader, unit="batch", leave=False) as tepoch:
					torch.cuda.empty_cache()
					for i, inputs in enumerate(tepoch):
						# (image, mask) = testingData
						tepoch.set_description(f"Testing Epoch {epoch}")
						# image, mask = image.to(device), mask.to(device)

						output = model.forward(inputs[0].to(device))

						loss = criterion(output, inputs[1].to(device))

						loss_value = loss.item()
						valLoss.append(loss_value)

						# tepoch.set_postfix(loss=loss.item(), accuracy=100. * train_acc)
						tepoch.set_postfix(loss=loss_value)

		print(f"Epochs: {epoch}\t Training Loss: {np.mean(trainLoss)}\t Testing Loss: {np.mean(valLoss)}")

		if (minValLoss is None) or (minValLoss > np.mean(valLoss)):
			minValLoss = np.mean(valLoss)
			torch.save(model.state_dict(), PATH+".pth")

		trainEpochLoss.append(np.mean(trainLoss))
		valEpochLoss.append(np.mean(valLoss))
		save_loss_image(trainEpochLoss, valEpochLoss, epoch, PATH)

	model.load_state_dict(torch.load(PATH +".pth"))
	# '''
	model.to("cpu")
	model.eval()

	dummy_input = torch.randn(1, 3, args['size'], args['size'])
	# dummy_input = torch.randn(1, 3, 512, 512)
	torch.onnx.export(model, dummy_input, f"{PATH}.onnx", verbose=True, opset_version=10)

if __name__ == "__main__":
	parser = argparse.ArgumentParser()
	parser.add_argument("-d", "--data", required=True, help="Information Related to train process")
	args = vars(parser.parse_args())
	with open(args['data'], "r") as f:
		args = eval(f.read())
	args['train_images'] = os.path.join(args['train_folder'], "images").replace("\\", "/")
	args['train_masks'] = os.path.join(args['train_folder'], "masks").replace("\\", "/")

	try:
		if os.path.isdir(args["val_folder"]):
			args['val_images'] = os.path.join(args['val_folder'], "images").replace("\\", "/")
			args['val_masks'] = os.path.join(args['val_folder'], "masks").replace("\\", "/")

			args['validation'] = True
	except Exception as e:
		args['validation'] = False

	print(args)
	train(args)


import math

# @param nums: The integer array
# @param target: Target number to find
# @return the first position of target in nums, position start from 0

def binarySearch(nums, target):
    low = 0
    high = len(nums) - 1
    while low <= high:
        mid = int((low + high) / 2)
        if high == low:
            return high
        if nums[mid] > target:
            high = mid - 1
        elif nums[mid] <= target:
            low = mid + 1
    return -1

print(binarySearch([2, 6, 8, 13, 15, 17, 17, 18, 19, 20], 15))


__all__ = ['SnapshotFilesWdg','SObjectDirListWdg']

from pyasm.common import Environment, Common, jsonloads, jsondumps
from pyasm.search import SearchKey, Search
from pyasm.biz import Snapshot, Pipeline
from pyasm.web import WebContainer, SpanWdg, DivWdg, Table
from pyasm.widget import IconWdg, SelectWdg

from tactic.ui.common import BaseRefreshWdg
from tactic.ui.tools import RepoBrowserDirListWdg
from tactic.ui.widget import DirListWdg, SingleButtonWdg, ButtonNewWdg, IconButtonWdg
from tactic.ui.container import DialogWdg, Menu, MenuItem, SmartMenu
from pyasm.biz import FileGroup

import os, re, six

class SnapshotFilesWdg(BaseRefreshWdg):
    '''This is used in the "Files" hidden row to display all the files of
    a snapshot'''

    def init(self):
        self.base_dir = None

    def get_files(self):

        paths = []

        # remember this here for now
        self.files = {}
        self.snapshots = {}

        search_key = self.kwargs.get("search_key")
        sobject = SearchKey.get_by_search_key(search_key)
        # if it is deleted, return
        if not sobject:
            return []

        if isinstance(sobject, Snapshot):
            snapshots = [sobject]
        else:
            snapshots = Snapshot.get_by_sobject(sobject, "publish")

        for snapshot in snapshots:
            snapshot_paths = snapshot.get_all_lib_paths()

            files = snapshot.get_all_file_objects()

            for path, file in zip(snapshot_paths, files):

                # if the path is a directory, get all of the files
                if os.path.isdir(path):
                    for root, dirnames, filenames in os.walk(path):

                        for filename in filenames:
                            item_path = "%s/%s" % (root, filename)
                            paths.append(item_path)
                            self.files[item_path] = file
                            self.snapshots[item_path] = snapshot

                        for dirname in dirnames:
                            item_path = "%s/%s/" % (root, dirname)
                            paths.append(item_path)
                            self.files[item_path] = file
                            self.snapshots[item_path] = snapshot

                    """
                    dirlist = os.listdir(path)
                    for item in dirlist:
                        item_path = "%s%s" % (path, item)
                        if os.path.isdir(path):
                            item_path = "%s/" % item_path
                        paths.append(item_path)
                        self.files[path] = file
                    """

                else:
                    paths.append(path)
                    self.files[path] = file
                    base_dir_alias =  file.get_value('base_dir_alias')
                    if not self.base_dir and base_dir_alias:
                        self.base_dir = Environment.get_asset_dir(alias=base_dir_alias)

        return paths

    def get_display(self):

        top = self.top
        top.add_style("padding: 10px")
        top.add_color("background", "background", -5)
        top.add_style("min-width: 600px")

        paths = self.get_files()

        # assume that all the paths are part of the same repo
        repo = 'tactic'
        for file in self.files.values():
            repo = file.get_value("repo_type", no_exception=True)
            break

        if repo == 'perforce':
            search_key = self.kwargs.get("search_key")
            sobject = SearchKey.get_by_search_key(search_key)

            project = sobject.get_project()
            depot = project.get_value("location", no_exception=True)
            if not depot:
                depot = project.get_code()
            location = '//%s' % depot

            dir_list = SnapshotDirListWdg(base_dir=location, location="scm", show_base_dir=True,paths=paths, all_open=True, files=self.files, snapshots=self.snapshots)
        else:
            # If not discovered thru base_dir_alias, use the default
            if not self.base_dir:
                self.base_dir = Environment.get_asset_dir()

            dir_list = SnapshotDirListWdg(base_dir=self.base_dir, location="server", show_base_dir=True,paths=paths, all_open=True, files=self.files, snapshots=self.snapshots)

        top.add(dir_list)

        return top

class SnapshotDirListWdg(DirListWdg):

    def __init__(self, **kwargs):
        if kwargs.get("all_open") == None:
            kwargs["all_open"] = True

        super(SnapshotDirListWdg, self).__init__(**kwargs)
        self.snapshots = kwargs.get("snapshots")
        if not self.snapshots:
            self.snapshots = {}

    def add_top_behaviors(self, top):

        # convert this to a repo directory
        asset_dir = Environment.get_asset_dir()
        web_dir = Environment.get_web_dir()

        web = WebContainer.get_web()
        browser = web.get_browser()
        use_applet = web.use_applet()

        if browser == 'Qt':
            top.add_relay_behavior( {
            'type': 'dblclick',
            'bvr_match_class': 'spt_dir_list_item',
            'cbjs_action': '''
            var path = bvr.src_el.getAttribute("spt_path");
            var asset_dir = '%s';
            var web_dir = '%s';
            var relative_dir = path.replace(asset_dir, "");
            var url = web_dir + "/" + relative_dir;

            var url_parts = url.split("/");
            var file = url_parts.pop();
            file = encodeURIComponent(file);
            url_parts.push(file);
            url = url_parts.join("/");

            var class_name = 'tactic.ui.widget.EmbedWdg';
            var kwargs = {
                src: url
            }

            var parts = path.split("/");
            var filename = parts[parts.length-1];
            spt.tab.set_main_body_tab()
            spt.tab.add_new(filename, filename, class_name, kwargs);
            ''' % (asset_dir, web_dir)
            } )
        else:
            top.add_relay_behavior( {
            'type': 'dblclick',
            'bvr_match_class': 'spt_dir_list_item',
            'cbjs_action': '''
            var path = bvr.src_el.getAttribute("spt_path");
            if (path.indexOf('####') != -1) {
                spt.info('Cannot open the file sequence');
            } else {
                var asset_dir = '%s';
                var web_dir = '%s';
                var relative_dir = path.replace(asset_dir, "");
                var url = web_dir + "/" + relative_dir;

                // Encode the filename
                var url_parts = url.split("/");
                var filename = url_parts.pop();
                filename = encodeURIComponent(filename);
                url_parts.push(filename);
                url = url_parts.join("/");

                window.open(url);
            }
            ''' % (asset_dir, web_dir)
            } )

        if use_applet:
            # add a top menu
            menu = Menu(width=180)
            menu_item = MenuItem(type='title', label='Actions')
            menu.add(menu_item)

            menu_item = MenuItem(type='action', label='Download to Folder')
            menu.add(menu_item)
            menu_item.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
                var activator = spt.smenu.get_activator(bvr);
                var applet = spt.Applet.get();
                var select_dir = true;
                var dir = applet.open_file_browser('', select_dir);
                if (!dir) {
                    dir = applet.get_current_dir();
                }
                if (!dir) {
                    spt.alert("No folder selected to copy to");
                    return;
                }

                var path = activator.getAttribute("spt_path");

                var asset_dir = '%s';
                var env = spt.Environment.get();
                var server_url = env.get_server_url();
                var url = server_url + "/assets/" + path.replace(asset_dir, "");
                var parts = path.split("/");
                var filename = parts[parts.length-1];
                spt.app_busy.show("Downloading file", filename);
                applet.download_file(url, dir + "/" + filename);
                spt.app_busy.hide();
                if (dir)
                    spt.notify.show_message("Download to '" + dir + "' completed.")
                ''' % asset_dir
            } )
            #menu_item = MenuItem(type='action', label='Check-out To Sandbox')
            #menu.add(menu_item)
            #menu_item.add_behavior( {
            #'type': 'click_up',
            #'cbjs_action': '''spt.alert('Not implemented yet.')'''
            #} )
            menu_item = MenuItem(type='action', label='Copy to Clipboard')
            menu.add(menu_item)
            menu_item.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            var activator = spt.smenu.get_activator(bvr);
            var path = activator.getAttribute("spt_path");

            var search_key = activator.getAttribute("spt_file_search_key");

            var server = TacticServerStub.get();
            var class_name = 'tactic.command.clipboard_cmd.ClipboardCopyCmd';
            var search_keys = [search_key];
            var kwargs = {
                search_keys: search_keys
            }
            try {
                spt.app_busy.show("Copy to Clipboard ...");
                server.execute_cmd(class_name, kwargs);
                spt.app_busy.hide();
                }
            catch(e) {
                spt.alert(spt.exception.handler(e));
            }
            '''
            } )

            menu_item = MenuItem(type='action', label='View Metadata')
            menu.add(menu_item)
            menu_item.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            var activator = spt.smenu.get_activator(bvr);
            var path = activator.getAttribute("spt_path");

            var search_key = activator.getAttribute("spt_file_search_key");

            var server = TacticServerStub.get();
            var class_name = 'tactic.ui.checkin.SnapshotMetadataWdg';
            var kwargs = {
                search_key: search_key
            }
            spt.panel.load_popup("Metadata", class_name, kwargs);
            '''
            } )

            menus_in = {
                'FILE_MENU_CTX': menu,
            }
            SmartMenu.attach_smart_context_menu( top, menus_in, False )

        self.add_selection(top)

        super(SnapshotDirListWdg, self).add_top_behaviors(top)

    def add_file_behaviors(self, item_div, dirname, basename):

        path = "%s/%s" % (dirname, basename)

        file_objects = self.kwargs.get("files")
        file_object = file_objects.get(path)
        if not file_object:
            print("WARNING: No file object for [%s]" % path)
            return
        file_search_key = file_object.get_search_key()

        item_div.add_class("spt_dir_list_item")
        item_div.add_attr("spt_path", path)
        item_div.add_attr("spt_file_search_key", file_search_key)
        SmartMenu.assign_as_local_activator( item_div, 'FILE_MENU_CTX' )

    def get_info(self, dirname, basename):
        location = self.kwargs.get("location")
        # get some info about the file
        path = "%s/%s" % (dirname, basename)

        snapshot = self.snapshots.get(path)
        file_range = None

        if FileGroup.is_sequence(path) and snapshot:
            file_range = snapshot.get_file_range()
            #start_frame = file_range.get_frame_start()
            #end_frame = file_range.get_frame_end()

        if location == 'server':
            self.info = Common.get_dir_info(path, file_range=file_range)
        else:
            self.info = {}
        return self.info

    def handle_item_div(self, item_div, dirname, basename):
        path = "%s/%s" % (dirname, basename)
        if self.info.get("file_type") == 'missing':
            icon_string = IconWdg.DELETE
            tip = 'Missing [%s]' %path
        else:
            icon_string = self.get_file_icon(dirname, basename)
            tip = path

        icon_div = DivWdg()
        icon_div.add_style("display: flex")

        item_div.add(icon_div)
        icon = IconWdg(tip, icon_string)
        icon_div.add(icon)
        icon_div.add_style("margin-right: 3px")

        # add the file name
        filename_div = DivWdg()
        item_div.add(filename_div)
        filename_div.add_style("float: left")
        filename_div.add_style("overflow: hidden")

        snapshot = self.snapshots.get(path)
        file_type = self.info.get('file_type')
        if file_type == 'sequence':
            if snapshot:
                file_range = snapshot.get_file_range()
                start_frame = file_range.get_frame_start()
                end_frame = file_range.get_frame_end()
                basename = re.sub(re.compile("#+"), "(%s-%s)" % (start_frame, end_frame), basename)
                #file_names = snapshot.get_expanded_file_names()
                filename_div.add(basename)
            else:
                filename_div.add(basename)
        else:
            filename_div.add(basename)

        if snapshot:
            snapshot_div = DivWdg()
            item_div.add(snapshot_div)
            snapshot_div.add_style("float: left")
            snapshot_div.add_style("font-style: italic")
            snapshot_div.add_style("opacity: 0.5")
            process = snapshot.get_value("process")
            version = snapshot.get_value("version")
            if version == 0:
                version = 'current'
            elif version == -1:
                version = 'lastest'
            else:
                version = 'v%s' % version
            snapshot_div.add("   (%s %s)" % (version, process))

            #size = snapshot.get_value("st_size")

        # Right now, the size is taken from the file system, however,
        # should we be reporting the database size?
        size = self.info.get('size')
        if not size:
            size = 0

        from pyasm.common import FormatValue
        size = FormatValue().get_format_value(size, 'KB')

        filesize_div = DivWdg()
        item_div.add(filesize_div)
        filesize_div.add(size)
        #filesize_div.add_style("width: 200px")
        filesize_div.add_style("text-align: right")
        filesize_div.add_style("margin-left: 15px")

    def get_file_icon(self, dir, item):
        if self.info.get("file_type") == 'link':
            return IconWdg.LINK
        return IconWdg.DETAILS

    def get_dir_icon(self, dir, item):
        return IconWdg.LOAD

    def add_selection(self, top):
        '''adding the behavior to select/unselect items'''
        top.add_behavior( {
            'type': 'load',
            'cbjs_action': '''

spt.selection = {};
spt.selection.top = null;

spt.selection.set_top = function(top) {
    spt.selection.top = top;
}

spt.selection.item_class = 'spt_dir_list_item';

spt.selection.get_all_items = function() {
    var items = spt.selection.top.getElements("."+spt.selection.item_class);
    return items;
}

spt.selection.select = function(el) {
    el.addClass("spt_select");
    el.setStyle("background", "#888");
    el.setAttribute("spt_background", '#888');
}

spt.selection.unselect = function(el) {
    el.removeClass("spt_select");
    el.setStyle("background", "");
    el.setAttribute("spt_background", '');
}

spt.selection.select_all_items = function() {
    var items = spt.selection.get_all_items();
    for (var i = 0; i < items.length; i++) {
        spt.selection.select(items[i]);
    }
}

spt.selection.unselect_all_items = function() {
    var items = spt.selection.get_all_items();
    for (var i = 0; i < items.length; i++) {
        spt.selection.unselect(items[i]);
    }
}

spt.selection.get_selected = function() {
    var items = spt.selection.top.getElements("."+spt.selection.item_class);
    var selected = [];
    for (var i = 0; i < items.length; i++) {
        if (items[i].hasClass("spt_select")) {
            selected.push(items[i]);
        }
    }
    return selected;
}

            '''
        } )

        top_class = 'spt_dir_list_top'
        item_class = 'spt_dir_list_item'

        # selection
        bg_color = top.get_color("background")
        hilight_color = top.get_color("background", -5)
        top.add_behavior( {
        'type': 'smart_click_up',
        'bvr_match_class': item_class,
        'cbjs_action': '''
        var top_class = '%s';
        var top = bvr.src_el.getParent("."+top_class);

        spt.selection.set_top(top);
        spt.selection.unselect_all_items();
        spt.selection.select(bvr.src_el);
        ''' % top_class
        } )

        # CTRL: toggle selection
        top.add_behavior( {
        'type': 'smart_click_up',
        'bvr_match_class': item_class,
        'modkeys': 'CTRL',
        'cbjs_action': '''
        var top_class = '%s';
        var top = bvr.src_el.getParent("."+top_class);

        spt.selection.set_top(top);
        spt.selection.select(bvr.src_el);
        ''' % top_class
        } )

        # SHIFT: select all items in between
        top.add_behavior( {
        'type': 'smart_click_up',
        'bvr_match_class': item_class,
        'modkeys': 'SHIFT',
        'cbjs_action': '''
        var top_class = '%s';
        var top = bvr.src_el.getParent("."+top_class);

        spt.selection.set_top(top);
        spt.selection.select(bvr.src_el);
        ''' % top_class
        } )

class SObjectDirListWdg(DirListWdg):
    '''Widget to display all the files in an sobject'''

    def get_files(self):

        paths = []

        # remember this here for now
        self.files = {}

        self.snapshots = {}

        search_key = self.kwargs.get("search_key")
        search_keys = self.kwargs.get("search_keys")
        if search_key:
            sobject = SearchKey.get_by_search_key(search_key)
            self.sobjects = [sobject]

        if search_keys:
            if isinstance(search_keys, six.string_types):
                search_keys = search_keys.replace("'", '"')
                search_keys = jsonloads(search_keys)
            self.sobjects = Search.get_by_search_keys(search_keys)

        if not self.sobjects:
            return []

        self.sobject = self.sobjects[0]

        for sobject in self.sobjects:

            if sobject.get_base_search_type() in ['sthpw/task', 'sthpw/note']:
                parent = sobject.get_parent()
                sobject_paths = self.get_sobject_files(parent)
                paths.extend(sobject_paths)

            else:
                sobject_paths = self.get_sobject_files(sobject)
                paths.extend(sobject_paths)

        return paths

    def get_sobject_files(self, sobject):
        paths = []

        show_versionless = self.kwargs.get("show_versionless")
        if show_versionless in [True, 'true']:
            show_versionless = True
        else:
            show_versionless = False

        if isinstance(sobject, Snapshot):
            snapshots = [sobject]
        else:
            # get the snapshots
            versions = self.get_value("versions")
            search = Search("sthpw/snapshot")

            search.add_parent_filter(sobject)

            if not versions or versions == 'latest':
                search.add_filter("is_latest", True)
            elif versions == 'current':
                search.add_filter("is_current", True)

            if show_versionless:
                search.add_filter("version", -1)
                search.add_op('or')

            processes = self.kwargs.get("processes")
            process = self.get_value("process")
            if process and process != 'all':
                search.add_filter("process", process)
            if processes:
                search.add_filters("process", processes)

            snapshots = search.get_sobjects()

            #snapshots = Snapshot.get_by_sobject(sobject)

        for snapshot in snapshots:

            exclude = ['web','icon']
            snapshot_paths = snapshot.get_all_lib_paths(exclude_file_types=exclude)
            files = snapshot.get_all_file_objects(exclude_file_types=exclude)
            for path, file in zip(snapshot_paths, files):

                # if the path is a directory, get all of the files
                if os.path.isdir(path):
                    for root, dirnames, filenames in os.walk(path):

                        for filename in filenames:
                            item_path = "%s/%s" % (root, filename)
                            paths.append(item_path)
                            self.files[item_path] = file

                        for dirname in dirnames:
                            item_path = "%s/%s/" % (root, dirname)
                            paths.append(item_path)
                            self.files[item_path] = file

                else:
                    paths.append(path)
                    self.snapshots[path] = snapshot
                    self.files[path] = file

        return paths

    def get_value(self, name):
        web = WebContainer.get_web()
        value = web.get_form_value(name)
        if not value:
            value = self.kwargs.get(name)
        return value

    def get_display(self):

        paths = self.get_files()

        top = self.top
        top.add_style("padding: 10px")
        top.add_color("background", "background")
        top.add_color("color", "color")
        top.add_style("min-width: 500px")
        top.add_style("font-size: 12px")
        top.add_class("spt_sobject_dir_list_top")
        self.set_as_panel(top)

        inner = DivWdg()
        top.add(inner)

        show_title = self.kwargs.get("show_title")
        if show_title not in [False, 'false']:
            title_wdg = DivWdg()
            inner.add(title_wdg)
            title_wdg.add("File Browser [%s]" % self.sobject.get_code())
            title_wdg.add_color("background", "background3")
            title_wdg.add_color("color", "color3")
            title_wdg.add_style("padding: 16px 10px")
            title_wdg.add_style("margin: -10px -10px 10px -10px")
            title_wdg.add_style("font-weight: bold")

        show_shelf = self.kwargs.get("show_shelf")
        if show_shelf not in [False, 'false']:
            shelf_wdg = DivWdg()
            inner.add(shelf_wdg)
            shelf_wdg.add(self.get_shelf_wdg())
            shelf_wdg.add_style("padding: 5px")
            shelf_wdg.add_style("margin: -5px -5px 15px -5px")
            shelf_wdg.add_style("font-weight: bold")

        base_dir = Environment.get_asset_dir()

        dir_list = SnapshotDirListWdg(base_dir=base_dir, location="server", show_base_dir=True,paths=paths, all_open=True, files=self.files, snapshots=self.snapshots)

        inner.add(dir_list)

        if self.kwargs.get("is_refresh"):
            return inner
        else:
            return top

    def get_shelf_wdg(self):

        process = self.get_value("process")
        versions = self.get_value("versions")

        div = DivWdg()

        filter_table = Table()
        div.add(filter_table)
        filter_table.add_row()

        button = SingleButtonWdg(title="Refresh", icon=IconWdg.REFRESH)
        filter_table.add_cell(button)
        filter_table.add_cell(" "*5)
        button.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            spt.panel.refresh(bvr.src_el);
            '''
        } )

        # get all of the pipelnes for this search type
        pipeline_code = self.sobject.get_value("pipeline_code", no_exception=True)
        processes = []
        if pipeline_code:
            pipeline = Pipeline.get_by_code(pipeline_code)
            if pipeline:
                process_names = pipeline.get_process_names()
                processes.extend(process_names)

        processes.insert(0, "all")

        filter_table.add_cell("Process: ")
        select = SelectWdg("process")
        select.add_style("width: 200px")
        if process != 'all':
            select.set_value(process)

        select.set_option("values", processes)

        filter_table.add_cell(select)

        filter_table.add_cell(" "*10)

        filter_table.add_cell("Versions: ")
        select = SelectWdg("versions")
        select.add_style("width: 200px")
        select.set_option("values", "latest|current|today|last 10|all")
        if versions:
            select.set_value(versions)
        filter_table.add_cell(select)

        asset_dir = Environment.get_asset_dir()

        select = IconButtonWdg( tip="Toggle Selection", icon=IconWdg.SELECT, show_arrow=False )
        div.add(select)
        select.add_style("float: right")
        select.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            var top_class = 'spt_sobject_dir_list_top'
            var toggle_state = bvr.src_el.getAttribute('toggle');
            if (toggle_state && toggle_state=='true')
                bvr.src_el.setAttribute('toggle','false');
            else
                bvr.src_el.setAttribute('toggle','true');

            var top = bvr.src_el.getParent("."+top_class);
            spt.selection.set_top(top);

            toggle_state = bvr.src_el.getAttribute('toggle');
            if (toggle_state == 'true')
                spt.selection.select_all_items();
            else
                spt.selection.unselect_all_items();

            '''
            } )

        show = IconButtonWdg( tip="Switch View", icon=IconWdg.VIEW, show_arrow=False )
        div.add(show)
        show.add_style("float: right")
        show.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            var top_class = 'spt_sobject_dir_list_top'
            var top = bvr.src_el.getParent("."+top_class);
            spt.selection.set_top(top);
            var els = top.getElements(".spt_file_dir_item");
            for (var i = 0; i < els.length; i++) {
                var el = els[i];
                if (el.getStyle("display") == "none") {
                    els[i].setStyle("display", "");
                }
                else {
                    els[i].setStyle("display", "none");
                }
            }
            var els = top.getElements(".spt_file_item");
            for (var i = 0; i < els.length; i++) {
                var el = els[i];
                if (el.getStyle("padding-left") == "6px") {
                    var padding = el.getAttribute("spt_padding_left");
                    el.setStyle("padding-left", padding);
                }
                else {
                    el.setStyle("padding-left", "6px");
                }

            }

            '''
        } )

        gear = IconButtonWdg( tip="Download", icon=IconWdg.DOWNLOAD, show_arrow=False )
        div.add(gear)
        gear.add_style("float: right")
        gear.add_behavior( {
            'type': 'click_up',
            'cbjs_action': '''
            spt.app_busy.show('Select a folder to download to...','');
            var top_class = 'spt_sobject_dir_list_top';
            var top = bvr.src_el.getParent("."+top_class);
            spt.selection.set_top(top);
            var items = spt.selection.get_selected();

            setTimeout( function() {
                var applet = spt.Applet.get();
                var select_dir =true;
                var dir = applet.open_file_browser('', select_dir);
                if (dir.length == 0)
                    dir = applet.get_current_dir();
                else
                    dir = dir[0];

                if (!dir) {
                    spt.alert("No folder selected to copy to.");
                    spt.app_busy.hide();
                    return;
                }
                if (items.length == 0){
                    spt.alert("Please select at least one file to download.");
                    spt.app_busy.hide();
                    return;
                }

                var asset_dir = '%s';
                for (var i = 0; i < items.length; i++) {
                    var path = items[i].getAttribute("spt_path");
                    var env = spt.Environment.get();
                    var server_url = env.get_server_url();
                    var url = server_url + "/assets/" + path.replace(asset_dir, "");
                    var parts = path.split("/");
                    var filename = parts[parts.length-1];
                    spt.app_busy.show("Downloading file", filename);
                    applet.download_file(url, dir + "/" + filename);
                }
                spt.app_busy.hide();
                if (dir)
                    spt.notify.show_message("Download to '" + dir + "' completed.")
            }, 100);

            ''' % asset_dir

        } )

        return div

__all__.append("SnapshotMetadataWdg")
class SnapshotMetadataWdg(BaseRefreshWdg):

    def get_display(self):

        search_key = self.kwargs.get("search_key")
        snapshot = self.kwargs.get("snapshot")

        if snapshot:
            self.snapshot = snapshot
        else:
            self.snapshot = SearchKey.get_by_search_key(search_key)

        assert self.snapshot

        metadata = self.snapshot.get_metadata()

        top = self.top
        top.add_color("background", "background")

        table = Table()
        table.set_max_width()
        top.add(table)
        table.set_unique_id()
        table.add_border()

        table.add_smart_styles("spt_cell", {
            'padding': '3px'
        } )

        tr = table.add_row()
        tr.add_color("background", "background", -5)
        th = table.add_header("Property")
        th.add_style("min-width: 200px")
        th.add_style("padding: 5px")
        th = table.add_header("Value")
        th.add_style("min-width: 400px")
        th.add_style("padding: 5px")

        keys = metadata.get("__keys__")
        if not keys:
            keys = metadata.keys()

        empty = False
        if not keys:
            empty = True
            keys = ['','','','','','','']
            table.add_smart_styles("spt_cell", {
                'height': '20px'
            } )

        for i, key in enumerate(keys):
            value = metadata.get(key)

            title = Common.get_display_title(key)

            tr = table.add_row()

            if i % 2:
                tr.add_color("background", "background")
                tr.add_color("color", "color")
            else:
                tr.add_color("background", "background", -8)
                tr.add_color("color", "color")

            td = table.add_cell()
            td.add_class("spt_cell")
            td.add(title)

            td = table.add_cell()
            td.add_class("spt_cell")
            td.add(value)

        if empty:
            div = DivWdg()
            top.add(div)
            div.add_style("height: 30px")
            div.add_style("width: 150px")
            div.add_style("margin-top: -110px")
            div.center()
            div.add("<b>No Metadata</b>")
            div.add_border()
            div.add_color("background", "background3")
            div.add_color("color", "color3")
            div.add_style("padding: 20px")
            div.add_style("text-align: center")

            top.add_style("min-height: 200px")

        return top

__all__.append("PathMetadataWdg")
class PathMetadataWdg(BaseRefreshWdg):

    ARGS_KEYS = {
        "search_key": {
            'description': "Search key used to extract metadata from",
            'type': 'TextWdg',
            'order': 0,
        },
        "path": {
            'description': "Path of image to be used to extract metadata",
            'type': 'TextWdg',
            'order': 1,
        },
    }

    def get_display(self):

        search_key = self.kwargs.get("search_key")
        path = self.kwargs.get("path")
        parser_str = self.kwargs.get("parser")
        use_tactic_tags = self.kwargs.get("use_tactic_tags")

        from pyasm.checkin import BaseMetadataParser, ParserImportError

        #parser_str = "EXIF"
        if parser_str:
            parser = BaseMetadataParser.get_parser(parser_str, path)
        else:
            parser = BaseMetadataParser.get_parser_by_path(path)

        if parser:
            try:
                if use_tactic_tags in ['true', True]:
                    metadata = parser.get_tactic_metadata()
                else:
                    metadata = parser.get_metadata()
            except ParserImportError as e:
                div = DivWdg()
                div.add_class("fa")
                div.add_class("fa-times-circle")
                div.add_style("font-size: 800%")
                div.add_style("color: slategrey")
                div.add_style("line-height: 100px")

                msg_div = DivWdg()
                msg_div.add(e)
                msg_div.add_style("font-size: 200%")
                msg_div.add_style("color: grey")

                top = DivWdg()
                top.add(div)
                top.add(msg_div)
                top.add_style("display: flex")
                top.add_style("flex-direction: column")
                top.add_style("justify-content: center")
                top.add_style("align-items: center")
                return top
        else:
            metadata = {}

        parser_title = parser.get_title()

        top = self.top
        top.add_color("background", "background")
        top.add_class("spt_metadata_top")

        shelf = DivWdg()
        top.add(shelf)
        from tactic.ui.widget import ActionButtonWdg
        button = ActionButtonWdg(title="Add Selected to Keywords", width="200")
        shelf.add(button)
        shelf.add_style("margin: 10px 0px")
        button.add_behavior( {
            'search_key': search_key,
            'cbjs_action': '''
            var top = bvr.src_el.getParent(".spt_metadata_top");
            var values = spt.api.get_input_values(top, null, true);
            var searchables = values.searchable;
            var items = [];
            for (var i = 0; i < searchables.length; i++) {
                if (searchables[i] == "") {
                    continue;
                }
                items.push(searchables[i]);
            }

            var server = TacticServerStub.get();

            var class_name = 'spt.modules.workflow.AssetAddMetadataToKeywordsCmd';
            var kwargs = {
                search_key: bvr.search_key,
                items: items,
            };
            server.p_execute_cmd(class_name, kwargs)
            .then( function() {
                spt.api.clear_inputs(top);
                spt.notify.show_message("Added Keywords");
            } )

            '''
        } )

        table = Table()
        table.add_style("width: 100%")
        #table.add_style("table-layout: fixed")
        top.add(table)
        table.set_unique_id()

        table.add_smart_styles("spt_cell", {
            'padding': '3px'
        } )

        tr, td = table.add_row_cell()
        td.add(parser_title)
        td.add_style("height: 20px")
        td.add_style("font-weight: bold")
        td.add_style("padding: 5px 3px")
        td.add_color("background", "background", -5)
        border_color = td.get_color("border")
        td.add_color("border-bottom", "solid 1px %s" % border_color)

        tr.add_class("tactic_hover")

        """
        tr = table.add_row()
        tr.add_color("background", "background", -5)
        th = table.add_header("Property")
        th.add_style("min-width: 200px")
        th.add_style("padding: 5px")
        th = table.add_header("Value")
        #th.add_style("min-width: 400px")
        th.add_style("padding: 5px")
        """

        keys = metadata.get("__keys__")
        if not keys:
            keys = list(metadata.keys())

        empty = False
        if not keys:
            empty = True
            keys = ['','','','','','','']
            table.add_smart_styles("spt_cell", {
                'height': '20px'
            } )

        keys.sort()

        for i, key in enumerate(keys):
            value = metadata.get(key)

            value = Common.process_unicode_string(value)

            if not isinstance(key, six.string_types):
                key = str(key)
            #title = Common.get_display_title(key)
            title = key

            tr = table.add_row()
            tr.add_class("tactic_hover")

            if i % 2:
                tr.add_color("background", "background", -2)
                tr.add_color("color", "color")
            else:
                tr.add_color("background", "background")
                tr.add_color("color", "color")

            td = table.add_cell()
            td.add_class("spt_cell")
            td.add(title)
            td.add_style("width: 300px")
            td.add_style("min-width: 200px")

            td = table.add_cell()
            td.add_class("spt_cell")

            if len(str(value)) > 500:
                inside = DivWdg()
                td.add(inside)
                value = value[:500]
                inside.add(value)
                inside.add_style("max-width: 600px")
            else:
                td.add(value)
            td.add_style("max-width: 600px")

            td.add_style("overflow: hidden")
            td.add_style("text-overflow: ellipsis")
            td.add_style("white-space: nowrap")

            td = table.add_cell()
            td.add_class("spt_cell")

            try:
                is_ascii = True
                for c in str(value):
                    if ord(c) > 128:
                        is_ascii = False
                        break
                if not is_ascii:
                    continue
            except Exception as e:
                print("WARNING: ", e)
                continue

            from pyasm.widget import CheckboxWdg
            checkbox = CheckboxWdg("searchable")
            checkbox.add_attr("spt_is_multiple", "true")
            td.add(checkbox)
            td.add_style("width: 40px")
            td.add_style("max-width: 30px")
            checkbox.set_option("value", "%s|%s|%s" % (parser_title,key,value))

        if empty:
            div = DivWdg()
            top.add(div)
            div.add_style("height: 30px")
            div.add_style("width: 150px")
            div.add_style("margin-top: -110px")
            div.center()
            div.add("<b>No Metadata</b>")
            div.add_border()
            div.add_color("background", "background3")
            div.add_color("color", "color3")
            div.add_style("padding: 20px")
            div.add_style("text-align: center")

            top.add_style("min-height: 200px")

        return top


#IN NEW VERSIONS of qtcreator:
    # add this file in Options -> Debugger -> GDB -> Extra Dumper Files

#IN OLD VERSIONS, this is loaded via .gdbinit and you need the python and end flags!
#python

#import sys
#sys.path.insert(0, '/home/mtoussai/opt/qtcreator-4.2.0-rc1/share/qtcreator/debugger')

from dumper import *

import math

def qdump__mlr__String(d, value):
    p = value["p"]
    N = int(value["N"])
    s = "'"
    for i in xrange(0, N):
        s += "%c" % int(p.dereference())
        p += 1
    s += "' [%i]" % N
    d.putValue(s)
    d.putNumChild(2)
    if d.isExpanded():
        with Children(d):
            d.putSubItem("N", value["N"])
            d.putSubItem("p", value["p"])

def qdump__LIST(d, value):
    p = value["p"]
    N = int(value["N"])
    s = "<%i>" %N
    d.putValue(s)
    m=N
    if m>10:
        m=10
    d.putNumChild(m+1)
    if d.isExpanded():
        with Children(d):
            for i in xrange(0, m):
                s = "(%i)" % i
                d.putSubItem(s, p.dereference())
                p += 1
                i += 1
#            d.putSubItem("p", p)

def qdump__mlr__Array(d, value):
    p = value["p"]
    N = int(value["N"])
    nd = int(value["nd"])
    d0 = int(value["d0"])
    d1 = int(value["d1"])
    d2 = int(value["d2"])
    if nd==0:
        s = "<>"
    if nd==1:
        s = "<%i>" %d0
    if nd==2:
        s = "<%i %i>"%(d0,d1)
    if nd==3:
        s = "<%i %i %i>"%(d0,d1,d2)
    d.putValue(s)
    m=N
    if m>10:
        m=10
    d.putNumChild(m+4)
    if d.isExpanded():
        with Children(d):
            d.putSubItem("N", value["N"])
            for i in xrange(0, m):
                if nd==1:
                    s = "(%i)" %(i)
                if nd==2:
                    s = "(%i,%i)"%(i/d1,i%d1)
                if nd==3:
                    s = "(%i,%i,%i)"%(i/(d1*d2),(i/d2)%d1,i%d2)
                d.putSubItem(s, p.dereference())
                p += 1
                i += 1
            d.putSubItem("p", value["p"])
            d.putSubItem("reference", value["reference"])
            d.putSubItem("special", value["special"])

def qdump__Node_typed(d, value):
    keys_N = int(value["keys"]["N"])
    keys_p = value["keys"]["p"]
    pars_N = int(value["parents"]["N"])
    pars_p = value["parents"]["p"]
    s = ""
    for i in xrange(0, keys_N):
        string = keys_p.dereference()
        string_N = int(string["N"])
        string_p = string["p"]
        for j in xrange(0, string_N):
            s += "%c" % int(string_p.dereference())
            string_p += 1
        keys_p += 1
        if(i<keys_N):
            s += " "
    s += "("
    for i in xrange(0, pars_N):
        par = pars_p.dereference()
        parkeys_N = par["keys"]["N"]
        parkeys_p = par["keys"]["p"]
        string = (parkeys_p+parkeys_N-1).dereference()
        string_N = int(string["N"])
        string_p = string["p"]
        for j in xrange(0, string_N):
            s += "%c" % int(string_p.dereference())
            string_p += 1
        pars_p += 1
        if(i<pars_N):
            s += " "
    s += ")"
    d.putValue(s)
    d.putNumChild(4)
    if d.isExpanded():
        with Children(d):
            d.putSubItem("value", value["value"])
            d.putSubItem("keys", value["keys"])
            d.putSubItem("parents", value["parents"])
            d.putSubItem("numChildren", value["numChildren"])
            d.putSubItem("parentOf", value["parentOf"])
            d.putSubItem("index", value["index"])
            d.putSubItem("container", value["container"])

def qdump__NodeL(d, value):
    qdump__LIST(d, value)

def qdump__Graph(d, value):
    p = value["p"]
    N = int(value["N"])
    s = "<%i>" % N
    d.putValue(s)
    m=N
    if m>10:
        m=10
    d.putNumChild(m+1)
    if d.isExpanded():
        with Children(d):
            for i in xrange(0, m):
                s = "(%i)" %i
                d.putSubItem(s, p.dereference())
                p += 1
            d.putSubItem("isNodeOfGraph", value["isNodeOfGraph"])
            d.putSubItem("isIndexed", value["isIndexed"])
#            d.putSubItem("p", value["p"])

def qdump__BodyL(d, value):
    qdump__LIST(d,value)

def qdump__ShapeL(d, value):
    qdump__LIST(d,value)

def qdump__JointL(d, value):
    qdump__LIST(d,value)

def qdump__ProxyL(d, value):
    qdump__LIST(d,value)

def qdump__ors__Vector(d, value):
    x=float(value["x"])
    y=float(value["y"])
    z=float(value["z"])
    s = "[%f %f %f]" % (x,y,z)
    d.putValue(s)
    d.putNumChild(1)
    if d.isExpanded():
        with Children(d):
            with SubItem(d, "length"):
                d.putValue(math.sqrt(x*x+y*y+z*z))
                d.putType("float")
                d.putNumChild(0)

def qdump__ors__Quaternion(d, value):
    w=float(value["w"])
    x=float(value["x"])
    y=float(value["y"])
    z=float(value["z"])
    s = "[%f %f %f %f]" % (w,x,y,z)
    d.putValue(s)
    d.putNumChild(1)
    if d.isExpanded():
        with Children(d):
            with SubItem(d, "degrees"):
                d.putValue(360.0/math.pi*math.acos(w))
                d.putType("float")
                d.putNumChild(0)

#end


import matplotlib.pyplot as plt
import numpy as np

def plot(x_points, y_points, route=None):
    x = np.zeros(len(route))
    y = np.zeros(len(route))
    for i in range(len(route)):
        x[i] = x_points[route[i]-1]
        y[i] = y_points[route[i]-1]
    plt.scatter(x_points, y_points)
    plt.plot(x, y)
    plt.show()

def plot_scores(scores):

    xs = np.arange(0, len(scores))

    plt.plot(xs, scores)

    plt.title("Distance evolution")
    plt.xlabel("Iteration")
    plt.ylabel("Distance")
    plt.show()

def plot_data(data):

    for a in data:

        fig, ax = plt.subplots()

        for b in data[a]:

            score_results = np.array(data[a][b])
            n_iterations = len(score_results[0])

            ys = np.mean(score_results, axis=0)
            ci = np.std(score_results, axis=0)
            xs = range(0, n_iterations)

            ax.plot(xs,ys, label=f"b={b}")
            ax.fill_between(xs, ys - ci, ys + ci, alpha=.1)

        ax.set_xlabel("Iterations")
        ax.set_ylabel("Scores")
        plt.title(f"a={a}")
        plt.legend()
        plt.show()

    for b in data[list(data.keys())[0]]:

        fig, ax = plt.subplots()

        for a in data:

            score_results = np.array(data[a][b])
            n_iterations = len(score_results[0])

            ys = np.mean(score_results, axis=0)
            ci = np.std(score_results, axis=0)
            xs = range(0, n_iterations)

            ax.plot(xs,ys, label=f"a={a}")
            ax.fill_between(xs, ys - ci, ys + ci, alpha=.1)

        ax.set_xlabel("Iterations")
        ax.set_ylabel("Scores")
        plt.title(f"b={b}")
        plt.legend()
        plt.show()

def plot_best_difference(data, optimal_score):

    fig, ax = plt.subplots()
    for a in data:

        means = []
        yerrors = []

        for b in data[a]:

            score_results = data[a][b]
            diffs = [min(scores) - optimal_score for scores in score_results]

            means.append(np.mean(diffs))
            yerrors.append(np.std(diffs))

        plt.errorbar(data[a].keys(), means, yerr=yerrors, label=f"a={a}", capsize=5)

    ax.set_title("Difference between optimal and calculated path length")
    ax.set_xlabel("b")
    ax.set_ylabel("Distance difference")

    ax.set_yscale("log")
    ax.set_xscale("log")

    plt.legend()
    plt.show()


from collections import deque

import gym
import numpy as np
from gym import spaces
from PIL import Image

class NoopResetEnv(gym.Wrapper):
    def __init__(self, env, noop_max=30):
        """Sample initial states by taking random number of no-ops on reset.
        No-op is assumed to be action 0.
        """
        gym.Wrapper.__init__(self, env)
        self.noop_max = noop_max
        self.override_num_noops = None
        assert env.unwrapped.get_action_meanings()[0] == 'NOOP'

    def reset(self):
        """ Do no-op action for a number of steps in [1, noop_max]."""
        self.env.reset()
        if self.override_num_noops is not None:
            noops = self.override_num_noops
        else:
            noops = self.unwrapped.np_random.randint(
                1, self.noop_max + 1)  # pylint: disable=E1101
        assert noops > 0
        obs = None
        for _ in range(noops):
            obs, _, done, _ = self.env.step(0)
            if done:
                obs = self.env.reset()
        return obs

class FireResetEnv(gym.Wrapper):
    def __init__(self, env):
        """Take action on reset for environments that are fixed until firing."""
        gym.Wrapper.__init__(self, env)
        assert env.unwrapped.get_action_meanings()[1] == 'FIRE'
        assert len(env.unwrapped.get_action_meanings()) >= 3

    def reset(self):
        self.env.reset()
        obs, _, done, _ = self.env.step(1)
        if done:
            self.env.reset()
        obs, _, done, _ = self.env.step(2)
        if done:
            self.env.reset()
        return obs

class EpisodicLifeEnv(gym.Wrapper):
    def __init__(self, env):
        """Make end-of-life == end-of-episode, but only reset on true game over.
        Done by DeepMind for the DQN and co. since it helps value estimation.
        """
        gym.Wrapper.__init__(self, env)
        self.lives = 0
        self.was_real_done = True

    def step(self, action):
        obs, reward, done, info = self.env.step(action)
        self.was_real_done = done
        # check current lives, make loss of life terminal,
        # then update lives to handle bonus lives
        lives = self.env.unwrapped.ale.lives()
        if lives < self.lives and lives > 0:
            # for Qbert somtimes we stay in lives == 0 condtion for a few frames
            # so its important to keep lives > 0, so that we only reset once
            # the environment advertises done.
            done = True
        self.lives = lives
        return obs, reward, done, info

    def reset(self):
        """Reset only when lives are exhausted.
        This way all states are still reachable even though lives are episodic,
        and the learner need not know about any of this behind-the-scenes.
        """
        if self.was_real_done:
            obs = self.env.reset()
        else:
            # no-op step to advance from terminal/lost life state
            obs, _, _, _ = self.env.step(0)
        self.lives = self.env.unwrapped.ale.lives()
        return obs

class MaxAndSkipEnv(gym.Wrapper):
    def __init__(self, env, skip=4):
        """Return only every `skip`-th frame"""
        gym.Wrapper.__init__(self, env)
        # most recent raw observations (for max pooling across time steps)
        self._obs_buffer = deque(maxlen=2)
        self._skip = skip

    def step(self, action):
        """Repeat action, sum reward, and max over last observations."""
        total_reward = 0.0
        last_info = {}
        done = None
        for _ in range(self._skip):
            obs, reward, done, info = self.env.step(action)
            self._obs_buffer.append(obs)
            total_reward += reward
            for key in info.keys():
                if 'reward' in key.lower():
                    info[key] = info[key] + last_info.get(key, 0)
            last_info = info
            if done:
                break
        max_frame = np.max(np.stack(self._obs_buffer), axis=0)

        return max_frame, total_reward, done, last_info

    def reset(self):
        """Clear past frame buffer and init. to first obs. from inner env."""
        self._obs_buffer.clear()
        obs = self.env.reset()
        self._obs_buffer.append(obs)
        return obs

class MaxEnv(gym.Wrapper):
    def __init__(self, env, skip=4):
        gym.Wrapper.__init__(self, env)
        # most recent raw observations (for max pooling across time steps)
        self._obs_buffer = deque(maxlen=2)

    def step(self, action):
        """Repeat action, sum reward, and max over last observations."""
        obs, reward, done, info = self.env.step(action)
        self._obs_buffer.append(obs)
        max_frame = np.max(np.stack(self._obs_buffer), axis=0)
        return max_frame, reward, done, info

    def reset(self):
        """Clear past frame buffer and init. to first obs. from inner env."""
        self._obs_buffer.clear()
        obs = self.env.reset()
        self._obs_buffer.append(obs)
        return obs

class SkipEnv(gym.Wrapper):
    def __init__(self, env, skip=4):
        """Return only every `skip`-th frame"""
        gym.Wrapper.__init__(self, env)
        self._skip = skip

    def step(self, action):
        total_reward = 0.0
        last_info = {}
        done = None
        obs = None
        for _ in range(self._skip):
            obs, reward, done, info = self.env.step(action)
            total_reward += reward
            for key in info.keys():
                if 'reward' in key.lower():
                    info[key] = info[key] + last_info.get(key, 0)
            last_info = info
            if done:
                break

        return obs, total_reward, done, last_info

class ClipRewardEnv(gym.RewardWrapper):
    def _reward(self, reward):
        """Bin reward to {+1, 0, -1} by its sign."""
        return np.sign(reward)

class WarpFrame(gym.ObservationWrapper):
    def __init__(self, env):
        """Warp frames to 84x84 as done in the Nature paper and later work."""
        gym.ObservationWrapper.__init__(self, env)
        self.res = 84
        self.observation_space = spaces.Box(
            low=0, high=255, shape=(self.res, self.res, 1))

    def _observation(self, obs):
        frame = np.dot(obs.astype('float32'), np.array(
            [0.299, 0.587, 0.114], 'float32'))
        frame = np.array(Image.fromarray(frame).resize((self.res, self.res),
                                                       resample=Image.BILINEAR), dtype=np.uint8)
        return frame.reshape((self.res, self.res, 1))

class FrameStack(gym.Wrapper):
    default_k = 3

    def __init__(self, env, k=None):
        """Buffer observations and stack across channels (last axis)."""
        gym.Wrapper.__init__(self, env)
        if k is None:
            k = FrameStack.default_k
        self.k = k
        self.frames = deque([], maxlen=k)
        shp = env.observation_space.shape
        # assert shp[2] == 1  # can only stack 1-channel frames
        self.observation_space = spaces.Box(
            low=0, high=255, shape=(shp[0], shp[1], k * shp[2]))

    def reset(self):
        """Clear buffer and re-fill by duplicating the first observation."""
        ob = self.env.reset()
        for _ in range(self.k):
            self.frames.append(ob)
        return self._observation()

    def step(self, action):
        ob, reward, done, info = self.env.step(action)
        self.frames.append(ob)
        return self._observation(), reward, done, info

    def _observation(self):
        assert len(self.frames) == self.k
        obs = np.concatenate(self.frames, axis=2)
        assert list(np.shape(obs)) == list(self.observation_space.shape)
        return obs

class SkipAndFrameStack(gym.Wrapper):
    def __init__(self, env, skip=4, k=4):
        """Equivalent to SkipEnv(FrameStack(env, k), skip) but more efficient"""
        gym.Wrapper.__init__(self, env)
        self.k = k
        self._skip = skip
        self.frames = deque([], maxlen=k)
        shp = env.observation_space.shape
        assert shp[2] == 1  # can only stack 1-channel frames
        self.observation_space = spaces.Box(
            low=0, high=255, shape=(shp[0], shp[1], k))

    def reset(self):
        """Clear buffer and re-fill by duplicating the first observation."""
        ob = self.env.reset()
        for _ in range(self.k):
            self.frames.append(ob)
        return self._observation()

    def step(self, action):
        total_reward = 0.0
        last_info = {}
        done = None
        for _ in range(self._skip):
            ob, reward, done, info = self.env.step(action)
            total_reward += reward
            for key in info.keys():
                if 'reward' in key.lower():
                    info[key] = info[key] + last_info.get(key, 0)
            last_info = info
            self.frames.append(ob)
            if done:
                break
        return self._observation(), total_reward, done, last_info

    def _observation(self):
        assert len(self.frames) == self.k
        return np.concatenate(self.frames, axis=2)

class ObsExpandWrapper(gym.Wrapper):
    def __init__(self, env):
        super().__init__(env)
        low = env.observation_space.low
        high = env.observation_space.high
        self.observation_space = gym.spaces.Box(low if np.isscalar(low) else np.asarray(low).item(0), high if np.isscalar(high) else np.asarray(high).item(0),
                                                shape=(
            env.observation_space.shape[0],
            env.observation_space.shape[1] if len(
                env.observation_space.shape) >= 2 else 1,
            env.observation_space.shape[2] if len(
                env.observation_space.shape) >= 3 else 1
        ))

    def reset(self):
        ob = super().reset()
        if ob.ndim == 1:
            ob = ob[:, np.newaxis, np.newaxis]
        elif ob.ndim == 2:
            ob = ob[:, :, np.newaxis]
        return ob

    def step(self, action):
        ob, r, d, _ = super().step(action)
        if ob.ndim == 1:
            ob = ob[:, np.newaxis, np.newaxis]
        elif ob.ndim == 2:
            ob = ob[:, :, np.newaxis]
        return ob, r, d, _

class NoopFrameskipWrapper(gym.Wrapper):
    def __init__(self, env, gamma=0.99):
        super().__init__(env)
        self.FRAMESKIP_ON_NOOP = 2
        self.gamma = gamma
        self.noop_phase = False
        self.skipped_already = 0

    def _is_noop(self, action):
        return action == 0

    def step(self, action):
        if self._is_noop(action):
            R = 0
            last_info = {}
            ob = None
            d = False
            for i in range(self.FRAMESKIP_ON_NOOP):
                ob, r, d, info = super().step(action)
                R += r
                for key in info.keys():
                    if 'reward' in key.lower():
                        info[key] = info[key] + last_info.get(key, 0)
                    last_info = info
                if d:
                    break
            return ob, R, d, last_info
        else:
            return super().step(action)

class BreakoutContinuousActionWrapper(gym.Wrapper):
    def __init__(self, env):
        super().__init__(env)
        self.action_space = spaces.Box(-1, 1, shape=[1])

    def step(self, action):
        if action < -1 / 3:
            action = 3
        elif action >= -1 / 3 and action <= 1 / 3:
            action = 0
        else:
            action = 2
        return self.env.step(action)

def wrap_deepmind(env, episode_life=True, clip_rewards=True):
    """Configure environment for DeepMind-style Atari.

    Note: this does not include frame stacking!"""
    assert 'NoFrameskip' in env.spec.id  # required for DeepMind-style skip
    if episode_life:
        env = EpisodicLifeEnv(env)
    env = NoopResetEnv(env, noop_max=30)
    env = MaxAndSkipEnv(env, skip=4)
    if 'FIRE' in env.unwrapped.get_action_meanings():
        env = FireResetEnv(env)
    env = WarpFrame(env)
    if clip_rewards:
        env = ClipRewardEnv(env)
    return env

def wrap_deepmind_with_framestack(env, episode_life=True, clip_rewards=True, framestack_k=4, frameskip_k=4, noop_max=30):
    """Configure environment for DeepMind-style Atari."""
    assert 'NoFrameskip' in env.spec.id  # required for DeepMind-style skip
    if episode_life:
        env = EpisodicLifeEnv(env)
    env = NoopResetEnv(env, noop_max=noop_max)
    env = MaxAndSkipEnv(env, skip=frameskip_k)
    if 'FIRE' in env.unwrapped.get_action_meanings():
        env = FireResetEnv(env)
    env = WarpFrame(env)
    if clip_rewards:
        env = ClipRewardEnv(env)
    env = FrameStack(env, k=framestack_k)
    return env

wrap_atari = wrap_deepmind_with_framestack