from scipy.spatial import distance as dist
from imutils.video import VideoStream
from imutils import face_utils
from threading import Thread
import numpy as np
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT']="hide"
import pygame
import argparse
import imutils
import time
import dlib
import cv2
import matplotlib.gridspec as gridspec
from collections import OrderedDict
from matplotlib import pyplot as plt
import sklearn
from sklearn.metrics import pairwise
from sklearn.metrics.pairwise import euclidean_distances
import multiprocessing

def cal_EAR(eye):
	dist1=dist.euclidean(eye[1],eye[5])
	dist2=dist.euclidean(eye[2],eye[4])
	dist3=dist.euclidean(eye[0],eye[3])

	ear=(dist1+dist2)/(2*dist3)

	return ear

def cal_MAR(m):
	dist4=dist.euclidean(mouth[13],mouth[19])
	dist5=dist.euclidean(mouth[14],mouth[18])
	dist6=dist.euclidean(mouth[15],mouth[17])

	mar=(dist4+dist5+dist6)/3

	return mar

def count(thresholded, segmented):
    chull = cv2.convexHull(segmented)

    extreme_top    = tuple(chull[chull[:, :, 1].argmin()][0])
    extreme_bottom = tuple(chull[chull[:, :, 1].argmax()][0])
    extreme_left   = tuple(chull[chull[:, :, 0].argmin()][0])
    extreme_right  = tuple(chull[chull[:, :, 0].argmax()][0])

    cX = int((extreme_left[0] + extreme_right[0]) / 2)
    cY = int((extreme_top[1] + extreme_bottom[1]) / 2)

    distance = pairwise.euclidean_distances([(cX, cY)], Y=[extreme_left, extreme_right, extreme_top, extreme_bottom])[0]
    maximum_distance = distance[distance.argmax()]

    radius = int(0.8 * maximum_distance)

    circumference = (2 * np.pi * radius)

    circular_roi = np.zeros(thresholded.shape[:2], dtype="uint8")

    cv2.circle(circular_roi, (cX, cY), radius, 255, 1)

    circular_roi = cv2.bitwise_and(thresholded, thresholded, mask=circular_roi)

    (cnts, _) = cv2.findContours(circular_roi.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    count = 0

    for c in cnts:
        (x, y, w, h) = cv2.boundingRect(c)

        if ((cY + (cY * 0.25)) > (y + h)) and ((circumference * 0.25) > c.shape[0]):
            count += 1

    return count

def run_avg(image, aWeight):
    global bg
    if bg is None:
        bg = image.copy().astype("float")
        return

    cv2.accumulateWeighted(image, bg, aWeight)

def segment(image, threshold=25):
    global bg
    diff = cv2.absdiff(bg.astype("uint8"), image)

    thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)[1]

    cnts, _ = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    if len(cnts) == 0:
        return
    else:
        segmented = max(cnts, key=cv2.contourArea)
        return (thresholded, segmented)

pygame.mixer.init()

ap=argparse.ArgumentParser()
ap.add_argument("-l","--landmark_predictor",required=True,help="path of landmark predictor")
ap.add_argument("-a","--alarm",type=str,default="",help="path to alarm.wav file")
ap.add_argument("-w","--webcam",type=int,default=0,help="webcam number")

args=vars(ap.parse_args())
print()
print("######### INPUT ARGUMENTS:- #########")
print()
print("Landmark identifier: ",args["landmark_predictor"])
print("Alarm file: ",args["alarm"])
print("Webcam: ",args["webcam"])

top,right,bottom,left=10, 450, 225, 690
aWeight=0.5
num_frames=0
eye_aspect_ratio_threshold=0.3
mouth_aspect_ratio_threshold=20
cont_frames=48
cont_frames_blink=20
cont_frames_yawn=25
count1=0
count2=0
count3=0
bg = None
alarm_trig=False
og_ear=[]

print()
print("######### LOADING FACIAL LANDMARK PREDICTOR #########")
detector=dlib.get_frontal_face_detector()
predictor=dlib.shape_predictor(args["landmark_predictor"])

(leftstart,leftend)=face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(rightstart,rightend)=face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
(mstart,mend)=face_utils.FACIAL_LANDMARKS_IDXS["mouth"]

print()
print("######### STARTING VIDEOSTREAM #########")
vs=VideoStream(src=args["webcam"]).start()
time.sleep(1.0)

FACIAL_LANDMARKS_IDXS = OrderedDict([
	("mouth", (48, 68)),
	("right_eyebrow", (17, 22)),
	("left_eyebrow", (22, 27)),
	("right_eye", (36, 42)),
	("left_eye", (42, 48)),
	("nose", (27, 35)),
	("jaw", (0, 17))
])

print()
print("######### INITIALIZING EAR THRESHOLD #########")
print()
print("Please sit in a position you would normally sit while driving for a few seconds.")
while count2<=100:
	frame=vs.read()
	frame=imutils.resize(frame,width=700)
	frame=cv2.flip(frame,1)

	gray=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

	gray=cv2.equalizeHist(gray)

	rects=detector(gray,0)

	for rect in rects:
		shape=predictor(gray,rect)
		shape=face_utils.shape_to_np(shape)

		for (name,(i,j)) in face_utils.FACIAL_LANDMARKS_IDXS.items():
			for (x,y) in shape[i:j]:
				cv2.circle(frame,(x,y),1,(0,0,255),-1)

			(x,y,w,h)=cv2.boundingRect(np.array(shape[i:j]))
			region_of_interest=frame[y:y+h,x:x+w]
			region_of_interest=imutils.resize(region_of_interest,width=600,inter=cv2.INTER_CUBIC)

		lefteye=shape[leftstart:leftend]
		righteye=shape[rightstart:rightend]
		mouth=shape[mstart:mend]

		left_ear=cal_EAR(lefteye)
		right_ear=cal_EAR(righteye)

		avg_ear=(left_ear+right_ear)/2

		left_eye_hull=cv2.convexHull(lefteye)
		right_eye_hull=cv2.convexHull(righteye)

		cv2.drawContours(frame,[left_eye_hull],-1,(0,255,0),1)
		cv2.drawContours(frame,[right_eye_hull],-1,(0,255,0),1)

		og_ear.append(avg_ear)

		count2+=1

sum=0.0
for i in range(len(og_ear)):
	sum=sum+og_ear[i]

temp=sum/len(og_ear)
eye_aspect_ratio_threshold=0.83*temp

print()
print("######### EAR THRESHOLD DETERMINED #########")
print()
print("Average EAR of driver: {:.2f}".format(temp))
print("EAR threshold of the driver: {:.2f}".format(eye_aspect_ratio_threshold))
print()
print("If for a continuous of 48 frames the EAR of driver remains below the threshold then alarm is triggered.")
print()
print("Raise your hand in the indicated box to turn the alarm off.")

while True:
	frame=vs.read()
	frame=imutils.resize(frame, width=700)
	frame=cv2.flip(frame,1)

	clone = frame.copy()

	(height, width) = frame.shape[:2]

	roi = frame[top:bottom, right:left]

	gray=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

	gray=cv2.equalizeHist(gray)

	gray=cv2.GaussianBlur(gray,(7,7),0)

	gray_roi=cv2.cvtColor(roi,cv2.COLOR_BGR2GRAY)
	gray_roi=cv2.GaussianBlur(gray_roi,(7,7),0)

	rects=detector(gray,0)
	cv2.putText(frame, "Press 'q' to exit", (500, 500),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)

	for rect in rects:
		shape=predictor(gray,rect)
		shape=face_utils.shape_to_np(shape)

		for (name,(i,j)) in face_utils.FACIAL_LANDMARKS_IDXS.items():
			for (x,y) in shape[i:j]:
				cv2.circle(frame,(x,y),1,(0,0,255),-1)

			(x,y,w,h)=cv2.boundingRect(np.array(shape[i:j]))
			region_of_interest=frame[y:y+h,x:x+w]
			region_of_interest=imutils.resize(region_of_interest,width=600,inter=cv2.INTER_CUBIC)

		lefteye=shape[leftstart:leftend]
		righteye=shape[rightstart:rightend]
		mouth=shape[mstart:mend]

		left_ear=cal_EAR(lefteye)
		right_ear=cal_EAR(righteye)

		avg_ear=(left_ear+right_ear)/2

		mar=cal_MAR(mouth)

		left_eye_hull=cv2.convexHull(lefteye)
		right_eye_hull=cv2.convexHull(righteye)
		# mouth_hull=cv2.convexHull(mouth)

		cv2.drawContours(frame,[left_eye_hull],-1,(0,255,0),1)
		cv2.drawContours(frame,[right_eye_hull],-1,(0,255,0),1)
		cv2.drawContours(frame,[mouth],-1,(0, 255, 0), 1)

		if num_frames < 30:
			run_avg(gray_roi, aWeight)
		else:
			hand = segment(gray_roi)
			if hand is not None:
				(thresholded, segmented) = hand
				cv2.drawContours(clone, [segmented + (right, top)], -1, (0, 0, 255))
				cv2.imshow("Thesholded", thresholded)
				x=count(thresholded, segmented)
				if x>3:
					count1=0
					alarm_trig=False
					pygame.mixer.music.stop()

		if avg_ear<eye_aspect_ratio_threshold:
			count1+=1
			if count1>=cont_frames:
				if not alarm_trig:
					alarm_trig=True
					if args["alarm"]!="":
						pygame.mixer.music.load(args["alarm"])
						pygame.mixer.music.play()
						print()

				cv2.putText(frame,"ALERT: DRIVER SLEEPY!!!",(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)

			elif count1>=cont_frames_blink:
				cv2.putText(frame,"YOU JUST BLINKED",(10,470),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,0,0),2)

		else:
			count1=0
			cv2.putText(frame,"DRIVER IS AWAKE",(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,255,0),2)

		cv2.putText(frame,"EAR: {:.2f}".format(avg_ear),(10,60),cv2.FONT_HERSHEY_TRIPLEX,0.7,(0,255,0),2)

		if mar>mouth_aspect_ratio_threshold:
			count3+=1
			if count3>=cont_frames_yawn:
				cv2.putText(frame,"YOU JUST YAWNED",(10,500),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,0,0),2)

		cv2.putText(frame,"MAR: {:.2f}".format(mar),(10,90),cv2.FONT_HERSHEY_TRIPLEX,0.7,(0,255,0),2)

		cv2.rectangle(frame, (left, top), (right, bottom), (0,255,0), 2)

		num_frames += 1

		cv2.imshow("Frame",frame)
		key=cv2.waitKey(1) & 0xFF

		if key==ord("q"):
			break
cv2.destroyAllWindows()
vs.stop()


from __future__ import division
import numpy as np
import glob
import re

lexicon = open("lex.csv","r")
lexicon = lexicon.read()
lexicon = lexicon.split("\r\n")
for index in range(len(lexicon)):
    lexicon[index]=lexicon[index].split(",")
    try:
        lexicon[index][1] = np.float32(lexicon[index][1])
    except:
        del(lexicon[index])

ideology_score={}
ideology = {}

for filename in glob.glob('Addresses/*.txt'):
    file = open(filename, 'r')
    address = file.read()
    address = address.replace('\n', ' ')
    address = address.replace('  ', ' ')
    address = address.lower()
    address = re.sub(r'\W+', ' ', address)
    address = address.split(" ")

    ct_dct={}
    for word in address:
        try:
            ct_dct[word]=ct_dct[word]+1
        except:
            ct_dct[word]=1

    score_array=[]
    for key, value in ct_dct.items():
        for index in range(len(lexicon)):
            if key == lexicon[index][0]:
                score_array.append([lexicon[index][1], value])
                break

    for index in range(len(score_array)):
        product = score_array[index][0] * score_array[index][1]
        try:
            total = total + product
            count = count + score_array[index][1]
        except:
            total = product
            count = score_array[index][1]

    final_score = total/count
    ideology_score[filename] = final_score

for title in glob.glob('Addresses/*.txt'):
    if (ideology_score[title] <= -0.02 and ideology_score[title] >=-1.00):
        ideology[title] = "Liberal"
    elif (ideology_score[title] > -.02 and ideology_score[title] < .02):
        ideology[title] = "Moderate"
    elif (ideology_score[title] >=.02 and ideology_score[title] <= 1.00):
        ideology[title] = "Conservative"
    else:
        ideology[title] = "error"

for key, value in ideology.items():
    print(key + " --> " + value)


import math

music_section_names = [
                        'ADV', 'NUM', 'ADJ', 'NOUN', 'VERB', 'ADP',
                        'AUX', 'PROPN', 'DET', 'PUNCT', 'PRON', 'CCONJ',
                        'X', 'PART', 'INTJ', 'SYM'
                    ]

number_of_section = len(music_section_names)
highest_connect = 70001
pnt_array = []

with open('test_data', 'r') as original_data:
    for pnt in original_data:
        numbers = pnt.split()

        # Skip if there's not exactly 3 items
        if len(numbers) != 3:
            continue

        # Scale points
        scale_factor = 1.6
        for i in range(len(numbers)):
            numbers[i] = str(eval(numbers[i]) * scale_factor)

        pnt_array.append(numbers[0]  + ' ' + numbers[1] + ' ' + numbers[2])

number_of_pnt = len(pnt_array)

with open('full_data', 'w+') as full_data:
    with open('meta-main.txt', 'w+') as meta_main:
        with open('0.txt', 'w+') as main_data:
            for enum_pnt in enumerate(pnt_array):
                section_index = math.floor(enum_pnt[0] * number_of_section / number_of_pnt)
                section = music_section_names[section_index]

                full_data.write('section' + str(section_index+1) + '/' + str(number_of_section) + '_' + section + '\n')
                full_data.write(enum_pnt[1] + ' ')

                if enum_pnt[0] == 0:
                    full_data.write('1\n')

                    # writing to meta-main
                    meta_main.write('0\n')
                    meta_main.write('0 ' + 'section' + str(section_index+1) + '/' + str(number_of_section) + '_' + section + '\n')
                else:
                    full_data.write('0\n')

                # writing to main data
                main_data.write(str(enum_pnt[0]) + ', ' + \
                    str(math.ceil(\
                        highest_connect * (number_of_pnt - enum_pnt[0]) / number_of_pnt
                    )) + '\n')


# Напишите программу, которая принимает на вход цифру, обозначающую день недели,
# и проверяет, является ли этот день выходным.

def weekday(number):
    if number == 6 or number ==7:
        print('Yes, this is a weekend day')
    elif number >=1 and number <=5:
        print('No, this is a working day')
    else:
        print('Error: the number is not in the range [1,7]')

weekday(int(input('Enter a number of a weekday and we will tell you if it is a weekend or a working day: ')))


import PyQt5.QtWidgets as QtWidgets

from prototypes.battle.source.battle.battleView import MainBattleWindow
from prototypes.battle.source.battle.land.landArmy import LandArmy
from prototypes.battle.source.battle.land.landBattleView import LandBattleView
from prototypes.battle.source.battle.land.landUnitInBattle import LandUnitInBattle
from prototypes.battle.source.config.config import Config

CONFIG_FILE = 'config.ini'

app = QtWidgets.QApplication([])

# load config files
window_config = Config(CONFIG_FILE)

# Set up a land battle
nation_uk = window_config.get_nation("uk")
nation_uk.computer = True
nation_fr = window_config.get_nation("france")

unit_militia_default = window_config.get_unit_type('Militia I')
current_unit = LandUnitInBattle(False, 'Charge', False, 50, 25, 1, unit_militia_default, nation_fr)
targeted_unit = LandUnitInBattle(False, 'Shoot', False, 75, 50, 1, unit_militia_default, nation_uk)

defender = LandArmy(False, None, nation_uk)
attacker = LandArmy(False, None, nation_fr)

initial_battle_state = dict(auto_combat=False, turn=0,
                            current_unit=current_unit, targetted_unit=targeted_unit,
                            defender=defender, attacker=attacker)

# start the battle view
mainWindow = MainBattleWindow(window_config)
mainWindow.ui = LandBattleView(mainWindow, initial_battle_state, None)
mainWindow.ui.setup_ui()
mainWindow.show()

app.exec_()


from substanced.catalog.indexes import PathIndex
from substanced.util import get_dotted_name
from substanced.objectmap import find_objectmap

def treesetify_catalog_pathindexes(root, registry): # pragma: no cover
    # to avoid having huge pickles
    objectmap = find_objectmap(root)

    index_oids = objectmap.get_extent(get_dotted_name(PathIndex))

    for oid in index_oids:
        pathindex = objectmap.object_for(oid)
        pathindex._not_indexed = objectmap.family.IF.TreeSet(
            pathindex._not_indexed)

def includeme(config):
    config.add_evolution_step(treesetify_catalog_pathindexes)


__author__ = 'cameronjavier'
import logging

class CustomAdapter(logging.LoggerAdapter):
    """
    This example adapter expects the passed in dict-like object to have a
    'connid' key, whose value in brackets is prepended to the log message.
    """
    def process(self, msg, kwargs):
        return '[%s] %s' % (self.extra['connid'], msg), kwargs

class init():
    def __init__(self, whoFrom):
        logging.basicConfig(filename="/var/www/motiv8/log/output.log",
                            level=logging.DEBUG,
                            format='%(asctime)s : %(message)s',
                            datefmt='%a, %d %b %Y %H:%M:%S')
        logger = logging.getLogger(__name__)
        # logger.addFilter(AppFilter(whoFrom))

        # syslog = logging.StreamHandler(stream)
        # formatter = logging.Formatter('%(asctime)s %(userId)s : %(message)s')
        # syslog.setFormatter(formatter)
        # logger.addHandler(syslog)

        # logger.info = lambda msg: logger.info(msg, extra={'userId': whoFrom})
        # logger.debug = lambda msg: logger.debug(msg, extra={'userId': whoFrom})
        # logger.warning = lambda msg: logger.warning(msg, extra={'userId': whoFrom})
        global adapter
        adapter = CustomAdapter(logger, {'connid': whoFrom})


# https://jakevdp.github.io/blog/2013/04/15/code-golf-in-python-sudoku/

# Write functions that, given an index 0 <= i < 81,
# return the indices of grid spaces in the same row,
# column, and box as entry i
def row_indices(i):
    start = i - i % 9
    return range(start, start + 9)

def col_indices(i):
    start = i % 9
    return range(start, start + 81, 9)

def box_indices(i):
    start = 27 * (i // 27) + 3 * ((i % 9) // 3)
    return [i for j in range(3) for i in range(start + 9 * j, start + 9 * j + 3)]

# compute and store the full set of connected indices for each i
connected = [(set.union(set(box_indices(i)),
                        set(row_indices(i)),
                        set(col_indices(i)))
              - set([i]))
             for i in range(81)]

# S(p) will recursively find solutions and "yield" them
def S(p):
    # First, find the number of empty squares and the number of
    # possible values within each square
    L = []
    for i in range(81):
        if p[i] == '0':
            vals = set('123456789') - set(p[n] for n in connected[i])
            if len(vals) == 0:
                return
            else:
                L.append((len(vals), i, vals))

    # if all squares are solved, then yield the current solution
    if len(L) == 0 and '0' not in p:
        yield p

    # otherwise, take the index with the smallest number of possibilities,
    # and recursively call S() for each possible value.
    else:
        N, i, vals = min(L)
        for val in vals:
            for s in S(p[:i] + val + p[i + 1:]):
                yield s

puz="027800061000030008910005420500016030000970200070000096700000080006027000030480007"
for s in S(puz):
    print(s)

puz = 81*'0'  # empty puzzle
print(next(S(puz)))

# def test(S):
#     # solve an empty puzzle
#     print(next(S(81*'0')))
#     print('')
#
#     # find all four solutions of puz
#     for s in S(puz):
#         print(s)
#
# test(S)


"""
downloadXkcd.py - download all xksd comics
from https://xkcd.com/
"""

import os, time
import requests
import bs4

base_url = 'https://xkcd.com'
upload_dir = 'xkcd'
img_selector = '#comic > img'
prev_selector = 'a[rel="prev"]'
images_to_load = 100

def parse_page(response, img_selector, prev_selectorl, url):
    """
    Return image and previous page urls.
    """
    soup = bs4.BeautifulSoup(response.text, 'html.parser')

    img_elem = soup.select(img_selector)
    if not img_elem:
        return None, None

    img_elem = img_elem[0]
    img_url = 'http:' + img_elem.get('src')

    prev_elem = soup.select(prev_selector)[0]
    prev_url = url + prev_elem.get('href')

    return img_url, prev_url

def download_image(img_url, path):
    """Download image from 'img_url' to directory set in 'path'"""

    image = requests.get(img_url)
    image.raise_for_status()

    os.makedirs(path, exist_ok=True)

    name = os.path.basename(img_url)
    load_path = os.path.join(path, name)
    with open(load_path, 'wb') as imgf:
        for chunk in image.iter_content(100_000):
            imgf.write(chunk)

images_loaded = unreadable = 0
prev_url = base_url

START_TIME = time.perf_counter()
#while not prev_url.endswith('#'):
for _ in range(images_to_load):
    response = requests.get(prev_url)
    response.raise_for_status()

    img_url, previous = parse_page(response, img_selector,
                                   prev_selector, base_url)
    if img_url is None:
        unreadable += 1
        continue

    download_image(img_url, upload_dir)
    images_loaded += 1
    print(f"Comics loaded: {images_loaded}/{images_to_load}", end='\r')
    prev_url = previous

END_TIME = time.perf_counter()

print(f"Loaded -- {images_loaded} comics, skipped -- {unreadable}")
print(f"Time taken: {END_TIME - START_TIME} seconds.")


import datetime
import math
import time

partitions = dict()

def p(number):
	return partition(number, number)

def partition(n, m):
	global partitions

	if n <= 1: # Recursive stopping condition
		return 1

	if m > n:
		return partition(n, n)

	if (n, m) in partitions:
		return partitions[(n, m)]

	total = 0
	for k in xrange(1, m + 1):
		total += partition(n - k, k)

	partitions[(n, m)] = total
	return total

def main():
	# The insight for this problem has been taken from https://en.wikipedia.org/wiki/Partition_%28number_theory%29#Algorithm
	print("The result is %d." % (p(100) - 1))

if __name__ == '__main__':
	start = time.time()
	main()
	done = time.time()
	print("The solution took %.4f seconds to compute." % (done - start))


from pwn import *

io = remote('128.199.234.122', 4200)

print(io.recv())
io.sendline('%6$x %7$x')
print(io.recv())
a = io.recv().split()
num1 = int(a[0], 16)
num2 = int(a[1], 16)
io.sendline(str(num1+num2))
print(io.recvall())


import csv
import numpy as np
import sqlite3
import json

def Round(a):
    return float(np.round(float(a), 4))

def convert_bool(a):
    if a == "True":
        return True
    return False

# Make a set of refs which are referred to by any source in either hm
lmxb = set()
hmxb = set()
with open("data/summaryA.csv") as file1:
    csv_reader = csv.DictReader(file1)
    for row in csv_reader:
        if row["class"] == "lmxb":

            for i in row["r_Opt"].split(','):
                if i == "":
                    continue
                lmxb.add(i.strip())
            for i in row["r_Fx"].split(','):
                if i == "":
                    continue
                lmxb.add(i.strip())
            for i in row["r_Vmag"].split(','):
                if i == "":
                    continue
                lmxb.add(i.strip())
            for i in row["r_Ppulse"].split(','):
                if i == "":
                    continue
                lmxb.add(i.strip())

        elif row["class"] == "hmxb":

            for i in row["r_Opt"].split(','):
                if i == "":
                    continue
                hmxb.add(i.strip())
            for i in row["r_Fx"].split(','):
                if i == "":
                    continue
                hmxb.add(i.strip())
            for i in row["r_Vmag"].split(','):
                if i == "":
                    continue
                hmxb.add(i.strip())
            for i in row["r_Ppulse"].split(','):
                if i == "":
                    continue
                hmxb.add(i.strip())
        else:
            print("UNKNOWN SOURCE")

hmxb_full = {}
lmxb_full = {}
with open("data/hmxbrefs.csv") as hmxbref_file:
    # use numpy to select only those rows  which have
    dr = csv.DictReader(hmxbref_file)
    for row in dr:
        hmxb_full[row["id"]] = (row["id"], row["bib"], row["Name"], row["desc"])

with open("data/lmxbrefs.csv") as lmxbref_file:
    # use numpy to select only those rows  which have
    dr = csv.DictReader(lmxbref_file)
    for row in dr:
        lmxb_full[row["id"]] = (row["id"], row["bib"], row["Name"], row["desc"])

hmxb_to_add = []
for i in hmxb:
    if i not in hmxb_full:
        continue
    t = hmxb_full[i]
    hmxb_to_add.append((t[0] + "_hmxb", t[1], t[2], t[3]))

lmxb_to_add = []
for i in lmxb:
    if i not in lmxb_full:
        continue
    t = lmxb_full[i]
    lmxb_to_add.append((t[0] + "_lmxb", t[1], t[2], t[3]))

hmxb_to_add = hmxb_to_add + lmxb_to_add

with open('data/publications.csv', 'r') as fin:
    dr = csv.DictReader(fin)
    to_db_pub = [(i['Idx'], i['Title'], i['Keywords'], i['Abstract'], i['Bib'], i['Authors']) for i in dr]

# sourceA -paper mapping tuple
src_pub = []
with open('data/summaryA.csv') as f:
    dr = csv.DictReader(f)
    for row in dr:
        if row['Papers'] == "":
            continue
        for ppr_id in row['Papers'].split(','):
            src_pub.append((int(row['Id']), int(ppr_id.strip())))

# To fill db with catalog B sources
with open('data/summaryB.csv', 'r') as fin:
    dr = csv.DictReader(fin)
    len_cat_B = 0
    to_db_src_B = [(i['idx'], str(i['object']), str(i['obsid']), Round(i['ra']), Round(i['dec']), str(i['ins']),
                    str(i['date_time']), str(i['proposal_id']), str(i['target_id']), i['abstract'], i['PI'])
                   for i in dr]

# To fill db with catalog A sources
with open('data/summaryA.csv', 'r') as fA:
    dr = csv.DictReader(fA)
    count_A = 0
    for i in dr:
        count_A += 1

print("in A ", count_A)

with open('data/summaryA.csv', 'r') as fA:
    l1 = []

    dr = csv.DictReader(fA)
    l1 = [(i['Id'], str(i['Name']), Round(i['RA']), Round(i['DE']), i['B-V'], i['Cat'], i['class'], i['E(B-V)'], i['Fx'],
           i['GLAT'], i['GLON'], i['Opt'], i['Porb'], i['Porb2'], i['Ppulse'], i['Range'], i['SpType'], i['Type'], i['U-B'], i['Vmag'],
           i['r_Fx'], i['r_Opt'], i['r_Ppulse'], i['r_Vmag']) for i in dr]

with open('data/observed_A.csv', 'r') as f_obs:
    l2 = []
    dr = csv.DictReader(f_obs)
    l2 = [(i["observed_uvit"], i["observed_sxt"], i["observed_laxpc"], i["observed_czti"], i['ID_A']) for i in dr]

to_db_src_A = []

for i in zip(l1, l2):
    a = i[0]
    b = i[1]
    to_db_src_A.append((a[0], a[1], a[2], a[3], convert_bool(b[0]), convert_bool(b[1]), convert_bool(b[2]), convert_bool(b[3]), a[4], a[5],
                        a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17], a[18], a[19], a[20], a[21], a[22], a[23]))

    # id,Name,RA,Dec,uvit,sxt,laxpc,czti,category

with open('data/observed_A_full.txt') as json_file:
    data = json.load(json_file)
    # dictionaries
    uvit = data['uvit']
    laxpc = data['laxpc']
    czti = data['czti']
    sxt = data['sxt']

obs_lax = []
obs_sx = []
obs_cz = []
obs_uv = []

for val in l2:
    uv = val[0]
    sx = val[1]
    lax = val[2]
    cz = val[3]
    id_src = val[4]
    if (uv == 'True'):
        c = uvit[str(id_src)]
        for src_B in c:
            obs_uv.append((id_src, src_B))
    if (cz == 'True'):
        c = czti[str(id_src)]
        for src_B in c:
            obs_cz.append((id_src, src_B))
    if (lax == 'True'):
        c = laxpc[str(id_src)]
        for src_B in c:
            obs_lax.append((id_src, src_B))
    if (sx == 'True'):
        c = sxt[str(id_src)]
        for src_B in c:
            obs_sx.append((id_src, src_B))

con = sqlite3.connect('db.sqlite3')
cursor = con.cursor()

# insert publication data to db
try:
    cursor.executemany("INSERT INTO source_publication(identifier,Name,Keywords,Abstract,Bib,Authors) VALUES(?,?,?,?,?,?)", to_db_pub)
    con.commit()
except Exception as error:
    print(error)

# insert catalog A data to db
try:
    cursor.executemany("INSERT INTO source_sourcea(id, Name,RA,Dec,isObserved_uvit,isObserved_sxt,isObserved_laxpc,isObserved_czti,B_V,Cat,Class,E_BV,Fx,GLAT,GLON,Opt,Porb,Porb2,Ppulse,Range,SpType,Type,U_B,Vmag,r_Fx,r_Opt,r_Ppulse,r_Vmag) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", to_db_src_A)
    con.commit()
except Exception as error:
    print(error)

# insert catalog B data to db
try:
    cursor.executemany(
        "INSERT INTO source_sourceb(id,Object,obsid,RA,Dec,instrument,date_time,proposal_id,target_id,observer,abstract) VALUES(?,?,?,?,?,?,?,?,?,?,?)", to_db_src_B)
    con.commit()
except Exception as error:
    print(error)

# insert common uvit data to db
try:
    cursor.executemany("INSERT INTO source_sourcea_uvit(sourcea_id,sourceb_id) VALUES(?,?)", obs_uv)
    con.commit()
except Exception as error:
    print(error)

# insert common sxt data to db
try:
    cursor.executemany("INSERT INTO source_sourcea_sxt(sourcea_id,sourceb_id) VALUES(?,?)", obs_sx)
    con.commit()
except Exception as error:
    print(error)

# insert common laxpc data to db
try:
    cursor.executemany("INSERT INTO source_sourcea_laxpc(sourcea_id,sourceb_id) VALUES(?,?)", obs_lax)
    con.commit()
except Exception as error:
    print(error)

# insert common czti data to db
try:
    cursor.executemany("INSERT INTO source_sourcea_czti(sourcea_id,sourceb_id) VALUES(?,?)", obs_cz)
    con.commit()
except Exception as error:
    print(error)

# insert hmxbrefs and lmxbrefs data  to db
try:
    cursor.executemany("INSERT INTO source_refs(id,bib,Name,desc) VALUES(?,?,?,?)", hmxb_to_add)
    con.commit()
except Exception as error:
    print(error)

# insert catalog B source and publication data to db
for entry in src_pub:
    try:
        cursor.execute("INSERT INTO source_sourcea_Publications(sourcea_id,publication_id) VALUES(?,?)", entry)
        con.commit()
    except Exception as error:
        print(error)

con.commit()

con.close()


import config
import torch
import torch.nn as nn
import transformers

def loss_fn(output, target, mask, num_labels):
    loss_function = nn.CrossEntropyLoss()
    active_loss = mask.view(-1) == 1
    active_logits = output.view(-1, num_labels)
    active_labels = torch.where(
            active_loss,
            target.view(-1),
            torch.tensor(loss_function.ignore_index).type_as(target)
    )
    # print("active logit shape: ", active_logits.shape)
    # print("active label shape: ", active_labels.shape)
    loss = loss_function(active_logits, active_labels)
    return loss

class NERBertModel(nn.Module):
    def __init__(self, num_tag):
        super().__init__()
        self.num_tag = num_tag
        self.bert = transformers.BertModel.from_pretrained(config.BASE_MODEL_PATH, return_dict=False)
        self.bert_drop_1 = nn.Dropout(0.3)

        # single layer
        self.out_tag = nn.Linear(768, self.num_tag)

        # multiple layer
        self.multi_layer = nn.Sequential(
            nn.Linear(768, 128),
            nn.Dropout(0.3),
            nn.ReLU(),
            nn.Linear(128, 32),
            nn.Dropout(0.3),
            nn.ReLU(),
            nn.Linear(32, self.num_tag),
        )

        # lstm version
        self.lstm = nn.LSTM(input_size=768, hidden_size=768, num_layers=2, dropout=0.3, bidirectional=True, batch_first=True)
        self.bidi_dim = 768 * 2
        self.lstm_lin_layer = nn.Linear(self.bidi_dim, self.num_tag)

    def forward(self, ids, mask, token_type_ids, target_tag, token_starts):
        out, _ = self.bert(ids, attention_mask=mask, token_type_ids=token_type_ids)

        dropout_tag = self.bert_drop_1(out)

        # single version
        tag = self.out_tag(dropout_tag)

        # multi version
        # tag = self.multi_layer(dropout_tag)

        ## lstm version
        # hidden, (last_hidden, last_cell) = self.lstm(out)
        # lstm_tag = self.lstm_lin_layer(hidden)
        # tag = lstm_tag

        loss_tag = loss_fn(tag, target_tag, mask, self.num_tag)
        loss = loss_tag

        return tag, loss


import itertools
import numpy
import tensorflow

def double_lu(alpha=-2):
    return lambda x: tensorflow.where(tensorflow.greater(x, 0), x / alpha, x * alpha)

class InvertibleAutoencoder():
    def __init__(self, input_layer):
        self.outputs = [input_layer]

    def add_dense(self,
                  kernel,
                  filters,
                  strides,
                  activation):
        input_shape = numpy.prod(kernel)
        output_shape = numpy.prod(filters)
        w_shape = input_shape * (input_shape + 1) / 2
        with tensorflow.variable_scope('layer_{0}'.format(len(self.outputs))):
            W = tensorflow.get_variable('weights', (w_shape,),
                                initializer=tensorflow.constant_initializer(1.0))
            s = 2 * W / (W ** 2 + 1)
            c = (1 - W ** 2) / (1 + W ** 2)
            j = 1
            i = 0
            final_shape = max(input_shape, output_shape)
            final_rotation = tensorflow.eye(final_shape)
            for c0, s0 in zip(tensorflow.unstack(c), tensorflow.unstack(s)):
                if i == j or i >= output_shape:
                    j += 1
                    i = 0
                if j >= output_shape:
                    break
                givens_matrix = tensorflow.SparseTensor(indices=[[i, i], [j, j], [i, j], [j, i]],
                                values=tensorflow.stack([c0, c0, -s0, s0]),
                                                        dense_shape=[final_shape, final_shape])
                final_rotation = tensorflow.sparse_tensor_dense_matmul(givens_matrix,
                                                                       final_rotation)
                i += 1
            final_rotation = tensorflow.slice(final_rotation, [0,] * 2, [output_shape, input_shape])
            bias = tensorflow.get_variable('bias_{0}'.format(len(self.outputs)),
                                output_shape,
                                initializer=tensorflow.constant_initializer(0.0))

            # convolution
            padded = tensorflow.pad(self.outputs[-1], [[k // 2, k // 2] for k in kernel])
            output_shape = [dim // stride for dim, stride in zip(self.outputs[-1].shape, strides)] + filters
            output = tensorflow.Variable(tensorflow.zeros(shape=output_shape, dtype=tensorflow.float32))
            for i in itertools.product(*[range(0, dim, stride) for dim, stride in zip(self.outputs[-1].shape, strides)]):
                k = [i0 // s for i0, s in zip(i, strides)]
                f = tensorflow.matmul(final_rotation,
                                      tensorflow.reshape(tensorflow.slice(padded,
                                                                          i,
                                                                          kernel),
                                                         [input_shape, 1]))
                tensorflow.scatter_nd_update(output, k, tensorflow.squeeze(f))
            self.outputs.append(activation(output + bias))

if __name__ == '__main__':
    a = InvertibleAutoencoder(tensorflow.placeholder(tensorflow.float32, shape=(128, 128)))
    a.add_dense([2, 2], [4], [2, 2], double_lu(2))


def ehTriangular(x):
  i = 0
  while i * (i+1) * (i+2) < n:
    i += 1
  return i * (i+1) * (i+2) == n

n = int(input("Digite um número natural: "))

while n >= 0:
  print(f'{n} {"é" if ehTriangular(n) else "não é"} triangular')
  n = int(input("Digite um número natural: "))


"""*********************************************************

NAME:     ex7

AUTHOR:   Paul Haddon Sanders IV, Ph.D

VERSION:  1. K-means Clustering

*********************************************************"""

import numpy as np
from scipy import io
from PIL import Image as im
from pdb import set_trace as pb

### Get data ###############################################

mat1  = io.loadmat('ex7data2.mat')
mat2  = io.loadmat('bird_small.mat')
mat3  = im.open('image.jpg')
X     = mat1['X']
A     = mat2['A']
B     = np.asarray(mat3)

Aproc     = np.resize(A,(A.shape[0]**2,A.shape[2]))

np.savetxt('kmeans.txt',Aproc)

Bproc     = np.resize(B,(B.shape[0]**2,B.shape[2]))

### 1.1 Implementing K-means ###############################

class kmeans:

    def __init__(self,k=3,n_iters=10):
        self.k       = k
        self.n_iters = n_iters

    def find_centroid(self,X):
        """Find centroid using K-means algorithm

        Parameters
        ----------
        X : {array-like}, shape = [n_samples,n_features]
          Training vectors, where n_samples is the number of samples and n_features is the number of features

        Returns
        -------
        self : object

        """

        final_distance = np.zeros(5)
        new_centroid   = np.zeros((5,self.k,X.shape[1]))

        """ Run the algorithm randomly several times """

        for iter1 in range(5):

            """ Randomly select centroids from (unique) X
                values """

            add_choice = X[np.random.choice(X.shape[0],self.k,replace=False)]

            choice = np.unique(add_choice,axis=0)

            while len(choice) < self.k:

                add_choice = X[np.random.choice(X.shape[0],self.k,replace=False)]

                choice = np.append(choice,add_choice,axis=0)
                choice = np.unique(choice,axis=0)

            new_centroid[iter1] = choice[:self.k]

            """ Proceed to find the new centroid """

            for iter2 in range(self.n_iters):

                """ Calculate distances """

                distances = np.array([((X - val)**2.0).sum(1) for val in new_centroid[iter1]])

                """ Find the nearest centroid position """

                centroid_number = distances.T.argmin(1)

                """ Get the new centroids by averaging """

                new_centroid[iter1] = np.array([X[np.where(centroid_number == val)].sum(0) / np.where(centroid_number == val)[0].shape[0] for val in range(self.k)])

            """ Check the error in the new centroid """

            distances = np.array([((X - val)**2.0).sum(1) for val in new_centroid[iter1]])
            centroid_number = distances.T.argmin(1)
            final_distance[iter1] = np.array([((X[np.where(centroid_number == i)] - val)**2.0).sum() for i,val in enumerate(new_centroid[iter1])]).sum()

        """ Select the centroid with the lowest error """

        self.centroid = new_centroid[final_distance.argmin()]

        return self

    def compress_data(self,X):

        distances = np.array([((X - val)**2.0).sum(1) for val in self.centroid])

        Xcopy = self.centroid[distances.T.argmin(1)]

        return Xcopy

    def change_pixel(self,pixel):

        distances = np.array([((pixel - val)**2.0).sum() for val in self.centroid])

        self.centroid[distances.T.argmin()] = pixel

        return self

### 1.4 Image compression with K-means #####################

km1 = kmeans(k=16)
km1.find_centroid(Aproc)

Acomp = km1.compress_data(Aproc)
Acomp = Acomp.astype('uint8')
Acomp = np.resize(Acomp,A.shape)

### 1.5 Use your own image #################################

km2  = kmeans(k=10)
km2.find_centroid(Aproc)
km2.change_pixel([255,255,255])

Bcomp     = km2.compress_data(Bproc)
Bcomp     = np.resize(Bcomp,B.shape)
Bcomp     = Bcomp.astype('uint8')

### output #################################################

image1 = im.new('RGB',(A.shape[0]*2,A.shape[0]))
image1_orig = im.fromarray(A)
image1_comp = im.fromarray(Acomp)

image1.paste(image1_orig,(0,0))
image1.paste(image1_comp,(Acomp.shape[0],0))

image1.show()

image2 = im.new('RGB',(B.shape[0]*2,B.shape[0]))
image2_orig = im.fromarray(B)
image2_comp = im.fromarray(Bcomp)

image2.paste(image2_orig,(0,0))
image2.paste(image2_comp,(Bcomp.shape[0],0))

image2.show()


import base64

supported_encoding_types = ['b16', 'b32', 'b64']

class EncodingTypeError(Exception):
    """Encoding type exception

    @ivar encoding_type: The encoding type that was specified
    @ivar supported_types: A list of the supported encoding types
    """
    def __init__(self, encoding_type, supported_types):
        message = "Invalid encoding type specified: %s.  Supported encoding" \
                  " types are: %s" % (encoding_type, supported_encoding_types)
        # Call the base class constructor with the parameters it needs
        Exception.__init__(self, message)

def encode_data(data, encoding="b64", url_safe=False):
    """Encode data using the base64 library.

    @param data: Data to be encoded
    @param encoding: Encoding type.  Supported types are 'b16', 'b32', and 'b64'
        The default is 'b64'.
    @param url_safe: Only valid if encoding is 'b64'.  If True, this causes the
        encoding to be performed by the urlsafe_b64encode function. Default is
        False
    @param data: Data to be encoded
    @raise EncodingTypeError: This exception occurs if an invalid encoding type
        is specified
    """
    encoded_data = ""
    if encoding in supported_encoding_types:
        if encoding == "b16":
            encoded_data = base64.b16encode(data)
        elif encoding == "b32":
            encoded_data = base64.b32encode(data)
        else:  # We assume that the encoding type is the default (b64)
            if url_safe:
                encoded_data = base64.urlsafe_b64encode(data)
            else:
                encoded_data = base64.b64encode(data)
        return encoded_data
    else:
        raise EncodingTypeError(encoding, supported_encoding_types)

def decode_data(encoded_data, encoding="b64", url_safe=False):
    """Decode previously encoded data using the base64 library or base64
    compatible library.  For security purposes, some optional parameters are
    not allowed in some of the supported encoding types.  See
    U{http://docs.python.org/library/base64.html}

    @param encoded_data: Encoded data that will be decoded
    @param encoding: Encoding type.  Supported types are 'b16', 'b32', and 'b64'
        The default is 'b64'.
    @param url_safe: Only valid if encoding is 'b64'.  If True, this causes the
        decoding to be performed by the urlsafe_b64decode function. Default is
        False
    @raise EncodingTypeError: This exception occurs if an invalid encoding type
        is specified
    @raise TypeError: This exception occurs if data is incorrectly padded or if
        there are non-alphabet characters present in the string.
    """
    decoded_data = ""
    if encoding in supported_encoding_types:
        if encoding == "b16":
            decoded_data = base64.b16decode(encoded_data)
        elif encoding == "b32":
            decoded_data = base64.b32decode(encoded_data)
        else:  # We assume that the encoding type is the default (b64)
            if url_safe:
                decoded_data = base64.urlsafe_b64decode(encoded_data)
            else:
                decoded_data = base64.b64decode(encoded_data)
        return decoded_data
    else:
        raise EncodingTypeError(encoding, supported_encoding_types)


import numpy as np
from scipy.spatial import cKDTree, distance_matrix
import os
import h5py
import vispy
import vispy.scene
from vispy.scene import visuals
from functools import partial
from scipy.spatial import cKDTree

# from tqdm import tqdm

def fps(x, num_points, idx=None):
    nv = x.shape[0]
    # d = distance_matrix(x, x)
    if idx is None:
        idx = np.random.randint(low=0, high=nv - 1)
    elif idx == 'center':
        c = np.mean(x, axis=0, keepdims=True)
        d = distance_matrix(c, x)
        idx = np.argmax(d)

    y = np.zeros(shape=(num_points, 3))
    indices = np.zeros(shape=(num_points,), dtype=np.int32)
    p = x[np.newaxis, idx, ...]
    dist = distance_matrix(p, x)
    for i in range(num_points):
        y[i, ...] = p
        indices[i] = idx
        d = distance_matrix(p, x)
        dist = np.minimum(d, dist)
        idx = np.argmax(dist)
        p = x[np.newaxis, idx, ...]
    return y, indices

def multi_res_fps_(x):
    num_points = x.shape[0]
    y = [x]
    while num_points > 1:
        num_points = num_points // 2
        fps_, _ = fps(y[-1], num_points, idx='center')
        y.append(fps_)
    return y

def multi_res_fps(X):
    num_samples = X.shape[0]
    num_points = X.shape[1]
    n = len(multi_res_fps_(X[0, ...]))
    Y = []
    for i in range(n):
        Y.append(np.zeros(shape=(num_samples, num_points, 3)))
        num_points = num_points // 2
    for i in range(num_samples):
        y = multi_res_fps_(X[i, ...])
        for j in range(len(y)):
            Y[j][i, ...] = y[j]
    return Y

def multi_res_fps_idx_(x):
    num_points = x.shape[0]
    y = [np.arange(num_points)]
    while num_points > 1:
        num_points = num_points // 2
        _, idx = fps(x, num_points, idx='center')
        y.append(idx)
    return y

def multi_res_fps_idx(X):
    num_samples = X.shape[0]
    num_points = X.shape[1]
    n = len(multi_res_fps_idx_(X[0, ...]))
    Y = []
    for i in range(n):
        Y.append(np.zeros(shape=(num_samples, num_points), dtype=np.int32))
        num_points = num_points // 2
    for i in range(num_samples):
        print(i / num_samples)
        y = multi_res_fps_idx_(X[i, ...])
        for j in range(len(y)):
            Y[j][i, ...] = y[j]
    return Y

def normalize(X):
    c = np.mean(X, axis=1, keepdims=True)
    X = np.subtract(X, c)
    r = np.multiply(X, X)
    r = np.sum(r, axis=-1, keepdims=True)
    r = np.max(r, axis=1, keepdims=True)
    r = np.maximum(np.sqrt(r), 0.00001)
    X = np.divide(X, r)
    return X

# Read numpy array data and label from h5_filename
def load_h5_data_label_seg(h5_filename):
    f = h5py.File(h5_filename)
    data = f['data'][:]
    label = f['label'][:]
    seg = f['pid'][:]
    return (data, label, seg)

# Read numpy array data and label from h5_filename
def load_h5(h5_filename):
    f = h5py.File(h5_filename)
    print(h5_filename)
    data = f['data'][:]
    label = f['label'][:]
    return (data, label)

def load_h5_files(data_path, files_list_path):
    files_list = [line.rstrip() for line in open(files_list_path)]
    data = []
    labels = []
    for i in range(len(files_list)):
        data_, labels_ = load_h5(os.path.join(data_path, files_list[i]))
        data.append(data_)
        labels.append(labels_)
    data = np.concatenate(data, axis=0)
    labels = np.concatenate(labels, axis=0)
    return data, labels

def save_h5(h5_filename, data, normals=None, subsamplings_idx=None, part_label=None,
            class_label=None, data_dtype='float32', label_dtype='uint8'):
    h5_fout = h5py.File(h5_filename)

    h5_fout.create_dataset(
        'data', data=data,
        compression='gzip', compression_opts=4,
        dtype=data_dtype)

    if normals is not None:
        h5_fout.create_dataset(
            'normal', data=normals,
            compression='gzip', compression_opts=4,
            dtype=data_dtype)

    if subsamplings_idx is not None:
        for i in range(len(subsamplings_idx)):
            name = 'sub_idx_' + str(subsamplings_idx[i].shape[1])
            h5_fout.create_dataset(
                name, data=subsamplings_idx[i],
                compression='gzip', compression_opts=1,
                dtype='int32')

    if part_label is not None:
        h5_fout.create_dataset(
            'pid', data=part_label,
            compression='gzip', compression_opts=1,
            dtype=label_dtype)

    if class_label is not None:
        h5_fout.create_dataset(
            'label', data=class_label,
            compression='gzip', compression_opts=1,
            dtype=label_dtype)
    h5_fout.close()

# Read numpy array data and label from h5_filename
def load_h5_data_multires(h5_filename, num_points):
    f = h5py.File(h5_filename)
    data = f['data'][:]
    sub_idx = []
    for i in range(len(num_points)):
        sub_idx.append(f['sub_idx_' + str(num_points[i])])

    class_label = f['class_label'][:]

    if 'part_label' in f:
        part_label = f['part_label'][:]
        return (data, sub_idx, part_label, class_label)
    else:
        return (data, sub_idx, class_label)

def multires_classif(path, file, tar_path):
    data, labels = load_h5(os.path.join(path, file))
    data = normalize(data)
    sub_idx = multi_res_fps_idx(data)
    save_h5(os.path.join(tar_path, file), data, subsamplings_idx=sub_idx, part_label=None,
            class_label=labels, data_dtype='float32', label_dtype='uint8')

def fps_classif(num_points, path, file, tar_path, multiscale=False):
    data, labels = load_h5(os.path.join(path, file))
    # data = normalize(data)
    X = []
    N = []
    num_pts = []
    n = num_points

    if multiscale:
        while n > 0:
            num_pts.append(n)
            n = int(n/2)
    else:
        num_pts = [n]
    num_samples = data.shape[0]
    for i in range(num_samples):
        Xi = []
        y = data[i, ...]
        for j in range(len(num_pts)):
            y, _ = fps(y, num_pts[j], idx=None)
            Xi.append(y)
        Xi = np.concatenate(Xi, axis=0)
        X.append(Xi)
        print(int(100 * i / num_samples))
    X = np.stack(X, axis=0)
    save_h5(os.path.join(tar_path, file), X, subsamplings_idx=None, part_label=None,
            class_label=labels, data_dtype='float32', label_dtype='uint8')

def load_h5_data_label_normal(h5_filename):
    f = h5py.File(h5_filename, mode='r')
    data = f['data'][:]
    label = f['label'][:]
    normal = f['normal'][:]
    return data, label, normal

def fps_classif_normals(num_points, path, file, tar_path, multiscale=False):
    data, labels, normal = load_h5_data_label_normal(os.path.join(path, file))
    # data = normalize(data)
    X = []
    N = []
    num_pts = []
    n = num_points

    if multiscale:
        while n > 0:
            num_pts.append(n)
            n = int(n/2)
    else:
        num_pts = [n]
    num_samples = data.shape[0]
    for i in range(num_samples):
        Xi = []
        Ni = []
        y = data[i, ...]
        n = normal[i, ...]
        for j in range(len(num_pts)):
            y, fps_idx = fps(y, num_pts[j], idx=None)
            n = n[fps_idx, ...]
            Xi.append(y)
            Ni.append(n)
        Xi = np.concatenate(Xi, axis=0)
        Ni = np.concatenate(Ni, axis=0)
        X.append(Xi)
        N.append(Ni)
        print(int(100 * i / num_samples))
    X = np.stack(X, axis=0)
    save_h5(os.path.join(tar_path, file), X, subsamplings_idx=None, part_label=None,
            class_label=labels, data_dtype='float32', label_dtype='uint8', normals=N)

def separate_classes(src_path, files_list, tar_path, file_name, classes):
    data, labels = load_h5_files(src_path, files_list)

    labels = labels[..., 0]

    idx = np.argsort(labels, axis=0)
    labels = labels[idx]
    data = data[idx]
    labels_ = labels.tolist()

    unique_labels = list(set(labels_))
    class_size = []
    for i in range(len(unique_labels)-1):
        class_size.append(labels_.count(unique_labels[i]))
    class_size = np.array(class_size, dtype=np.int32)
    class_size = np.cumsum(class_size)
    data = np.split(data, class_size, axis=0)
    labels = np.split(labels, class_size, axis=0)

    """
    classes = ['airplane', 'bathtub', 'bed', 'bench', 'bookshelf', 'bottle',
               'bowl', 'car', 'chair', 'cone', 'cup', 'curtain', 'desk',
               'door', 'dresser', 'flower_pot', 'glass_box', 'guitar', 'keyboard',
               'lamp', 'laptop', 'mantel', 'monitor', 'night_stand', 'person',
               'piano', 'plant', 'radio', 'range_hood', 'sink', 'sofa', 'stairs',
               'stool', 'table', 'tent', 'toilet', 'tv_stand', 'vase', 'wardrobe', 'xbox']
    """

    for i in range(len(data)):
        save_h5(os.path.join(tar_path, file_name + '_' + classes[i] + '.h5'), data[i], subsamplings_idx=None,
                part_label=labels[i],
                class_label=None, data_dtype='float32', label_dtype='uint8', normals=None)

def multires_part_seg(path, file, tar_path):
    f = h5py.File(os.path.join(path, file))
    data = normalize(f['data'][:])
    sub_idx = multi_res_fps_idx(data)
    part_labels = f['pid'][:]
    class_labels = f['label'][:]
    save_h5(h5_filename=os.path.join(tar_path, file), data=data, subsamplings_idx=sub_idx,
            part_label=part_labels, class_label=class_labels)

def read_off(file):
    file = open(file, 'r')
    line = file.readline().strip()
    if 'OFF' != line:
        line = line[3:]
        # raise ('Not a valid OFF header')
    else:
        line = file.readline().strip()

    n_verts, n_faces, n_dontknow = tuple([int(s) for s in line.split(' ')])
    verts = []

    for i_vert in range(n_verts):
        verts.append(np.array([float(s) for s in file.readline().strip().split(' ')]))
    verts = np.stack(verts)

    faces = []
    for i_face in range(n_faces):
        faces.append([int(s) for s in file.readline().strip().split(' ')][1:])
    faces = np.stack(faces).astype(np.int32)

    return verts, faces

def sample_faces(vertices, faces, n_samples=10 ** 4):
    """
    Samples point cloud on the surface of the model defined as vectices and
    faces. This function uses vectorized operations so fast at the cost of some
    memory.

    Parameters:
    vertices  - n x 3 matrix
    faces     - n x 3 matrix
    n_samples - positive integer

    Return:
        vertices - point cloud

    Reference :
        [1] Barycentric coordinate system

        \begin{align}
        P = (1 - \sqrt{r_1})A + \sqrt{r_1} (1 - r_2) B + \sqrt{r_1} r_2 C
        \end{align}
    """
    vec_cross = np.cross(vertices[faces[:, 0], :] - vertices[faces[:, 2], :],
                         vertices[faces[:, 1], :] - vertices[faces[:, 2], :])
    face_areas = np.sqrt(np.sum(vec_cross ** 2, 1))
    face_areas = face_areas / np.sum(face_areas)

    # Sample exactly n_samples. First, oversample points and remove redundant
    # Contributed by Yangyan (yangyan.lee@gmail.com)
    n_samples_per_face = np.ceil(n_samples * face_areas).astype(int)
    # floor_num = np.sum(sample_num_per_face) - n_samples
    floor_num = np.sum(n_samples_per_face) - n_samples
    if floor_num > 0:
        indices = np.where(n_samples_per_face > 0)[0]
        floor_indices = np.random.choice(indices, floor_num, replace=True)
        n_samples_per_face[floor_indices] -= 1

    n_samples = np.sum(n_samples_per_face)

    # Create a vector that contains the face indices
    sample_face_idx = np.zeros((n_samples,), dtype=int)
    acc = 0
    for face_idx, _n_sample in enumerate(n_samples_per_face):
        sample_face_idx[acc: acc + _n_sample] = face_idx
        acc += _n_sample

    r = np.random.rand(n_samples, 2)
    A = vertices[faces[sample_face_idx, 0], :]
    B = vertices[faces[sample_face_idx, 1], :]
    C = vertices[faces[sample_face_idx, 2], :]
    P = (1 - np.sqrt(r[:, 0:1])) * A + np.sqrt(r[:, 0:1]) * (1 - r[:, 1:]) * B + np.sqrt(r[:, 0:1]) * r[:, 1:] * C
    return P, sample_face_idx

def read_and_sample_off(file_path, num_samples=10 ** 4):
    V, F = read_off(file_path)
    return sample_faces(V, F, n_samples=num_samples)

def transfer_labels(x, y, labels):
    T = cKDTree(y)
    _, idx = T.query(x, k=1)

    return labels[idx, ...]

def compute_triangle_normals(V, F):
    xt = np.take(V, F, axis=0)
    u = xt[:, 1, :] - xt[:, 0, :]
    v = xt[:, 2, :] - xt[:, 0, :]
    n = np.cross(u, v)
    n_norm = np.sqrt(np.maximum(np.sum(np.multiply(n, n), axis=-1, keepdims=True), 0.00001))
    n = np.divide(n, n_norm)
    return n

def hdf5_from_off(num_points, tar_path, data_folder, part_labels_folder=None, class_labels_folder=None, n_split=5):
    # list directory
    names_ = []
    for file in os.listdir(data_folder):
        if file.endswith(".off"):
            names_.append(file[:-4])

    # names_ = names_[0:5]

    # split names

    names = []
    num_samples = len(names_)
    b_size = num_samples // n_split
    for i in range(n_split):
        start = i * b_size
        end = min((i + 1) * b_size, num_samples)
        names.append(names_[start:end])

    # print(names[4][23])
    # exit(666)

    original_labels = set([])
    labels_set = set([])
    part_labels = []
    data = []
    normals = []
    avg_num_labels = 0.
    num_shapes = 0.
    for i in range(n_split):
        data.append(np.zeros(shape=(len(names[i]), num_points, 3)))
        normals.append(np.zeros(shape=(len(names[i]), num_points, 3)))
        part_labels.append(np.zeros(shape=(len(names[i]), num_points), dtype=np.int32))
        for j in range(len(names[i])):
            print(i, j)

            V, F = read_off(os.path.join(data_folder, names[i][j] + '.off'))
            nT = compute_triangle_normals(V, F)
            x, face_idx = sample_faces(V, F, n_samples=10000)
            # x = read_and_sample_off(os.path.join(data_folder, names[i][j] + '.off'), num_samples=3*num_points)
            x, fps_idx = fps(x, num_points, idx='center')
            face_idx = face_idx[fps_idx, ...]
            data[i][j, ...] = x
            normals[i][j, ...] = nT[face_idx, ...]

            with open(os.path.join(part_labels_folder, names[i][j] + '.txt'), 'r') as f:
                label = [int(line) for line in f]
                l = set(label)
                original_labels = original_labels.union(l)
                num_shapes += 1.
                avg_num_labels += len(l)
                print(len(l))
                if -1 in label:
                    print(names[i][j])
                labels_set = labels_set.union(l)
            label = np.array(label, np.int32)

            label = transfer_labels(x=x, y=V, labels=label)

            part_labels[i][j, ...] = label

    print('avg_num_labels ', avg_num_labels / num_shapes)

    sub_idx = []

    for i in range(n_split):
        data[i] = normalize(data[i])
        # sub_idx.append(multi_res_fps_idx(data[i]))
        # print('sub_idx shape ', sub_idx[i][0].shape)

    """
    labels_set = set([])
    part_labels = []
    for i in range(len(names)):
        part_labels.append(np.zeros(shape=(len(names[i]), num_points)))
        for j in range(len(names[i])):
            with open(os.path.join(part_labels_folder, names[i][j]+'.txt'), 'r') as f:
                label = [line for line in f]
                labels_set = labels_set.union(set(label))
            label = np.array(label)

            label = transfer_labels(x=data[i][j, ...], )
            part_labels[i][j, ...] = np.array(label, dtype=np.int32)
    """

    labels_set = list(labels_set)

    original_labels = list(original_labels)

    # for i in range(len(original_labels)):

    print('num_labels')
    print(len(labels_set))
    print(labels_set)

    for i in range(n_split):
        for j in range(len(names[i])):
            print(i, j)
            print(part_labels[i].shape)
            for k in range(part_labels[i].shape[1]):

                part_labels[i][j, k] = labels_set.index(part_labels[i][j, k])

    class_labels = []

    for i in range(len(names)):
        class_labels.append(np.zeros(shape=(len(names[i]),), dtype=np.int32))

    for i in range(n_split):
        save_h5(h5_filename=os.path.join(tar_path, 'data_hdf5_' + str(i) + '.h5'),
                data=data[i], normals=normals[i], subsamplings_idx=None,
                part_label=part_labels[i], class_label=None)

def setup_pcl_viewer(X, color=(1, 1, 1, .5), run=False):
    # setup a point cloud viewer using vispy and return a drawing function
    # make a canvas and add simple view
    canvas = vispy.scene.SceneCanvas(keys='interactive', show=True)
    view = canvas.central_widget.add_view()
    # create scatter object and fill in the data
    # init_pc = np.random.normal(size=(100, 3), scale=0.2)
    init_pc = X
    scatter = visuals.Markers()
    draw_fn = partial(scatter.set_data, edge_color=None, face_color=color, size=5)
    draw_fn(init_pc)
    view.add(scatter)
    # set camera
    view.camera = 'turntable'  # ['turntable','arcball']
    # add a colored 3D axis for orientation
    axis = visuals.XYZAxis(parent=view.scene)
    return draw_fn

"""
rna_path = 'C:/Users/adrien/Documents/Datasets/RF00001-family'
rna_data_path = os.path.join(rna_path, 'off')
rna_part_labels_path = os.path.join(rna_path, 'labels')
rna_tar_path = os.path.join(rna_path, 'hdf5_data_with_normals_4096')

hdf5_from_off(num_points=4096, tar_path=rna_tar_path, data_folder=rna_data_path,
                  part_labels_folder=rna_part_labels_path,
                  class_labels_folder=None, n_split=5)
"""

modelnet40_rot_path = 'C:/Users/adrien/Documents/Datasets/ModelNet40_hdf5/modelnet40_hdf5_2048_original_rotated'
modelnet40_rot_data_path = os.path.join(modelnet40_rot_path, 'data_hdf5')
modelnet40_rot_tar_path = os.path.join(modelnet40_rot_path, 'data_hdf5_multires')

"""
modelnet40_files = ['test_data_0.h5', 'test_data_1.h5',
                    'train_data_0.h5', 'train_data_1.h5',
                    'train_data_2.h5', 'train_data_3.h5', 'train_data_4.h5']
"""
"""
for i in range(len(modelnet40_files)):
    multires_classif(modelnet40_rot_data_path, modelnet40_files[i], modelnet40_rot_tar_path)
"""
"""
modelnet40_path = 'E:/Users/Adrien/Documents/Datasets/ModelNet40_hdf5/modelnet40_hdf5_1024_original'
modelnet40_data_path = os.path.join(modelnet40_path, 'data_hdf5')
# modelnet40_tar_path = os.path.join(modelnet40_path, 'data_hdf5_multires')
# modelnet40_tar_path = "E:/Users/Adrien/Documents/Datasets/ModelNet40_hdf5/modelnet40_hdf5_1024_fps_multiscale/data_hdf5"
modelnet40_tar_path = "E:/Users/Adrien/Documents/Datasets/ModelNet40_hdf5/modelnet40_hdf5_1024_classes/data_hdf5"

for i in range(len(modelnet40_files)):
    fps_classif_normals(1024, modelnet40_data_path, modelnet40_files[i], modelnet40_tar_path, multiscale=False)

modelnet40_files = ['train_data_0.h5', 'train_data_1.h5',
                    'train_data_2.h5', 'train_data_3.h5', 'train_data_4.h5']

separate_classes(modelnet40_data_path, 'E:/Users/adrien/Documents/Datasets/ModelNet40_hdf5/train_files.txt', modelnet40_tar_path, 'train')
"""

"""
data, sub_idx, class_label = load_h5_data_multires(os.path.join(modelnet40_rot_tar_path, 'test_data_1.h5'),
                                                               [1024, 256, 64])
"""

"""
multires_from_off(num_points=4096, tar_path=rna_tar_path, data_folder=rna_data_path,
                  part_labels_folder=rna_part_labels_path,
                  class_labels_folder=None, n_split=5)
"""
"""
data, sub_idx, part_label, class_label = load_h5_data_multires(os.path.join(rna_tar_path, 'data_hdf5_0.h5'),
                                                               [4096, 1024, 128])
x = data[0, ...]
print(x.shape)
print(sub_idx[2].shape)
x = x[sub_idx[2][0, ...]]
print(x.shape)
# print(sub_idx[1])
setup_pcl_viewer(x)
vispy.app.run()
"""

"""
P = read_and_sample_off(os.path.join(rna_data_path, '1C2X_C.off'), num_samples=10**4)
print('aa')
x, _ = fps(x=P, num_points=2**12)
x = np.expand_dims(x, axis=0)
x = normalize(x)
x = x[0, ...]
print('aa')
y = multi_res_fps_idx_(x)
print(x[y[0]].shape)

setup_pcl_viewer(x[y[0]])
vispy.app.run()
"""

"""
Prepare shape net classifiaction
"""
"""
shapenet_source_path = "E:/Users/Adrien/Documents/Datasets/shapenet_segmentation/hdf5_data"
shapenet_target_path = "E:/Users/Adrien/Documents/Datasets/shapenet_classifiaction"

files_source = ["ply_data_train0.h5",
                "ply_data_train1.h5",
                "ply_data_train2.h5",
                "ply_data_train3.h5",
                "ply_data_train4.h5",
                "ply_data_train5.h5",
                "ply_data_val0.h5",
                "ply_data_test0.h5",
                "ply_data_test1.h5"]

for i in range(len(files_source)):
    fps_classif(1024, shapenet_source_path, files_source[i], shapenet_target_path, multiscale=False)
"""

shapenet_path = 'E:/Users/Adrien/Documents/Datasets/shapenet_classifiaction'

# modelnet40_tar_path = os.path.join(modelnet40_path, 'data_hdf5_multires')
# modelnet40_tar_path = "E:/Users/Adrien/Documents/Datasets/ModelNet40_hdf5/modelnet40_hdf5_1024_fps_multiscale/data_hdf5"
shapenet_tar_path = "E:/Users/Adrien/Documents/Datasets/shapenet_single_class/data_hdf5"

modelnet40_files = ['ply_data_test0.h5',
                    'ply_data_test1.h5',
                    'ply_data_train0.h5',
                    'ply_data_train1.h5',
                    'ply_data_train2.h5',
                    'ply_data_train3.h5',
                    'ply_data_train4.h5',
                    'ply_data_train4.h5',
                    'ply_data_val0.h5']

separate_classes(shapenet_path,
                 'E:/Users/Adrien/Documents/Datasets/shapenet_classifiaction/val_files.txt',
                 shapenet_tar_path,
                 'val', classes=["aero", "bag", "cap", "car", "chair",
                                   "earph", "guitar", "knife", "lamp", "laptop", "motor", "mug",
                                   "pistol", "rocket", "skate", "table"])


import torch
from util import pad
from metrics import compute_metrics

def compute_validation_outputs(model, val_iter, field, optional_names=[]):
    loss, predictions, answers = [], [], []
    outputs = [[] for _ in range(len(optional_names))]
    for batch_idx, batch in enumerate(val_iter):
        l, p = model(batch)
        loss.append(l)
        predictions.append(pad(p, 150, dim=-1, val=field.vocab.stoi['<pad>']))
        a = None
        if hasattr(batch, 'wikisql_id'):
            a = batch.wikisql_id.data.cpu()
        elif hasattr(batch, 'squad_id'):
            a = batch.squad_id.data.cpu()
        elif hasattr(batch, 'woz_id'):
            a = batch.woz_id.data.cpu()
        else:
            a =  pad(batch.answer.data.cpu(), 150, dim=-1, val=field.vocab.stoi['<pad>'])
        answers.append(a)
        for opt_idx, optional_name in enumerate(optional_names):
            outputs[opt_idx].append(getattr(batch, optional_name).data.cpu())
    loss = torch.cat(loss, 0) if loss[0] is not None else None
    predictions = torch.cat(predictions, 0)
    answers = torch.cat(answers, 0)
    return loss, predictions, answers, [torch.cat([pad(x, 150, dim=-1, val=field.vocab.stoi['<pad>']) for x in output], 0) for output in outputs]

def get_clip(val_iter):
    return -val_iter.extra if val_iter.extra > 0 else None

def all_reverse(tensor, world_size, field, clip, dim=0):
    if world_size > 1:
        tensor = tensor.float() # tensors must be on cpu and float for all_gather
        all_tensors = [torch.zeros_like(tensor) for _ in range(world_size)]
        torch.distributed.barrier() # all_gather is experimental for gloo, found that these barriers were necessary
        torch.distributed.all_gather(all_tensors, tensor)
        torch.distributed.barrier()
        tensor = torch.cat(all_tensors, 0).long() # tensors must be long for reverse
    # for distributed training, dev sets are padded with extra examples so that the
    # tensors are all of a predictable size for all_gather. This line removes those extra examples
    return field.reverse(tensor)[:clip]

def gather_results(model, val_iter, field, world_size, optional_names=[]):
    loss, predictions, answers, outputs = compute_validation_outputs(model, val_iter, field, optional_names=optional_names)
    clip = get_clip(val_iter)
    if not hasattr(val_iter.dataset.examples[0], 'squad_id') and not hasattr(val_iter.dataset.examples[0], 'wikisql_id') and not hasattr(val_iter.dataset.examples[0], 'woz_id'):
        answers = all_reverse(answers, world_size, field, clip)
    return loss, all_reverse(predictions, world_size, field, clip), answers, [all_reverse(x, world_size, field, clip) for x in outputs],

def print_results(keys, values, rank=None, num_print=1):
    print()
    start = rank * num_print if rank is not None else 0
    end = start + num_print
    values = [val[start:end] for val in values]
    for ex_idx in range(len(values[0])):
        for key_idx, key in enumerate(keys):
            value = values[key_idx][ex_idx]
            v = value[0] if isinstance(value, list) else value
            print(f'{key}: {repr(v)}')
        print()

def validate(task, val_iter, model, logger, field, world_size, rank, num_print=10, args=None):
    with torch.no_grad():
        model.eval()
        required_names = ['greedy', 'answer']
        optional_names = ['context', 'question']
        loss, predictions, answers, results = gather_results(model, val_iter, field, world_size, optional_names=optional_names)
        predictions = [p.replace('UNK', 'OOV') for p in predictions]
        names = required_names + optional_names
        if hasattr(val_iter.dataset.examples[0], 'wikisql_id') or hasattr(val_iter.dataset.examples[0], 'squad_id') or hasattr(val_iter.dataset.examples[0], 'woz_id'):
            answers = [val_iter.dataset.all_answers[sid] for sid in answers.tolist()]
        metrics, answers = compute_metrics(predictions, answers, bleu='iwslt' in task or 'multi30k' in task, dialogue='woz' in task,
            rouge='cnn' in task, logical_form='sql' in task, corpus_f1='zre' in task, args=args)
        results = [predictions, answers] + results
        print_results(names, results, rank=rank, num_print=num_print)

        return loss, metrics


def parse_minutes(time_str):
    h, m = time_str[:-3].split(':')
    if(time_str[-2:] == "PM"):
        return int(h) * 60 + int(m) + 720
    else:
        return int(h) * 60 + int(m)

def parse_time(time_str, days):
    if "TBA" in time_str or "TBA" in days:
        return
    times = []
    dayInMinutes = 1440
    dayDict = {
            "M": 0,
            "T": dayInMinutes,
            "W": dayInMinutes * 2,
            "R": dayInMinutes * 3,
            "Th": dayInMinutes * 3,
            "F": dayInMinutes * 4,
            "S": dayInMinutes * 5,
            "U": dayInMinutes * 6}
    startTime, endTime = time_str.split('-')
    startTime = parse_minutes(startTime)
    endTime = parse_minutes(endTime)
    if days.find("Th") == 1:
        addDayMin = int(dayDict["Th"])
        times.append((startTime+addDayMin, endTime+addDayMin))
        days = days.replace("Th", "")
    for day in days:
        addDayMin = int(dayDict[day])
        times.append((startTime+addDayMin, endTime+addDayMin))
    return times

class Section:
    def __init__(self, classObj):
        self.crn = classObj[0]['CRN']
        self.times = []
        for oneClass in classObj:
            tempTime = parse_time(oneClass['time'], oneClass['days'])
            if tempTime:
                for time in tempTime:
                    self.times.append(time)

    def __str__(self):
        return self.crn

    def __repr__(self):
        return self.crn


import numpy as np
import cv2
import math

from utils import find_best_line, preprocess_image

def detect_lines(image):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    lower_blue = np.array([100, 50, 50])
    upper_blue = np.array([130, 255, 255])

    mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
    res_blue = cv2.bitwise_and(image, image, mask=mask_blue)
    canny_blue = cv2.Canny(res_blue, 50, 200, None, 3)

    lines_blue = cv2.HoughLinesP(canny_blue, 1, np.pi / 180, 50, None, 50, 20)

    detected_line_points = [[(0,0), (0,0)], [(0,0), (0,0)]]
    if lines_blue is not None:
        print("[INFO]: Detected {} blue lines in the given frame.".format(len(lines_blue)))

        if len(lines_blue) < 2:
            print("[ERROR]: Detected less then 2 blue lines in the given frame.")
            exit(1)

        detected_line_points[0][0], detected_line_points[0][1] = find_best_line(lines_blue)
        cv2.line(image, (detected_line_points[0][0][0], detected_line_points[0][0][1]), (detected_line_points[0][1][0], detected_line_points[0][1][1]),(0, 0, 255), 1, cv2.LINE_AA)

    lower_green = np.array([60, 100, 100])
    upper_green = np.array([70, 255, 255])

    mask_green = cv2.inRange(hsv, lower_green, upper_green)
    res_green = cv2.bitwise_and(image, image, mask=mask_green)
    canny_green = cv2.Canny(res_green, 50, 200, None, 3)

    lines_green = cv2.HoughLinesP(canny_green, 1, np.pi / 180, 50, None, 50, 20)

    if lines_green is not None:
        print("[INFO]: Detected {} green lines in the given frame.".format(len(lines_green)))

        if len(lines_green) < 2:
            print("[ERROR]: Detected less then 2 green lines in the given frame.")
            exit(1)

        detected_line_points[0][0], detected_line_points[0][1] = find_best_line(lines_blue)
        cv2.line(image, (detected_line_points[1][0][0], detected_line_points[1][0][1]),
                 (detected_line_points[1][1][0], detected_line_points[1][1][1]), (0, 0, 255), 1, cv2.LINE_AA)

    return detected_line_points

def resize_region(region):
    return cv2.resize(region, (28, 28), interpolation=cv2.INTER_NEAREST)

def select_roi(image_orig, image_bin):
    img, contours, hierarchy = cv2.findContours(image_bin.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    regions_array = []
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)  # koordinate i velicina granicnog pravougaonika
        if (h > 12 and w >= 12) or (h > 12 and w <= 4 and w >= 2):
            region = image_bin[y:y + h + 1, x:x + w + 1]
            regions_array.append([resize_region(region), (x, y, w, h)])

    regions_array = sorted(regions_array, key=lambda x: x[1][0])

    sorted_regions = []
    sorted_regions_coords = []
    for region in regions_array:
        sorted_regions.append(region[0])
        sorted_regions_coords.append(region[1])

    return sorted_regions,sorted_regions_coords,

def search_for_detection(detections, detection):
    indices = []
    i = 0
    for d in detections:
        (X1, Y1) = detection.center
        (X2, Y2) = d.center

        distance = math.sqrt(math.pow((X1 - X2),2) + math.pow((Y1 - Y2),2))

        if distance < 20:
            indices.append(i)

        i += 1

    return indices


"""
2021.4
idea:
1. recursive with cache
    - boundary: res[0]=1, res[x<0]=0
    - recursive: res[x] = sum([res[x-num] for num in nums:])
2. DP (from top to bottom)
    - set dp list
    - dp states equation: dp[x] += dp[x-num] for num in nums
"""

from typing import List
from functools import wraps

class Solution:
    def __init__(self):
        self.cache = {}
    def combinationSum4(self, nums: List[int], target: int) -> int:
        if target < 0:
            return 0
        if target == 0:
            return 1
        res = 0
        for num in nums:
            x = target - num
            if x not in self.cache:
                self.cache[x] = self.combinationSum4(nums, x)
            res += self.cache[x]
        return res

    def combinnationSum4_dp(self, nums, target):
        dp = [0] * (target+1)
        dp[0] = 1
        res = 0
        for i in range(target+1):
            for num in nums:
                if i >= num:
                    dp[i] += dp[i-num]
        return dp[target]

if __name__ == '__main__':
    nums = [1,2,3]
    target = 4
    S = Solution()
    print(S.combinationSum4_recursive(nums, target))