import tensorflow as tf
import numpy as np
import math
import time
from pixel_cnn_pp.nn import *

def lrelu(x, rate=0.1):
    # return tf.nn.relu(x)
    return tf.maximum(tf.minimum(x * rate, 0), x)

def fc_lrelu(inputs, num_outputs):
    fc = tf.contrib.layers.fully_connected(inputs, num_outputs,
                                           weights_initializer=tf.random_normal_initializer(stddev=0.02),
                                           activation_fn=tf.identity)
    fc = lrelu(fc)
    return fc

def mlp_discriminator(x, reuse=False):
    with tf.variable_scope('d_net') as vs:
        if reuse:
            vs.reuse_variables()
        x = tf.reshape(x, [-1, np.prod(x.get_shape().as_list()[1:])])
        fc1 = fc_lrelu(x, 512)
        fc2 = fc_lrelu(fc1, 512)
        fc3 = tf.contrib.layers.fully_connected(fc2, 1, activation_fn=tf.identity)
        return fc3

class ConvolutionalEncoder(object):
    def __init__(self, X, reg_type, latent_dim, z=None):
        '''
            This is the 6-layer architecture for Convolutional Autoencoder
            mentioned in the original paper:
            Stacked Convolutional Auto-Encoders for Hierarchical Feature Extraction

            Note that only the encoder part is implemented as PixelCNN is taken
            as the decoder.
        '''
        self.x = X
        conv1 = conv2d(X, 64, [4, 4], [2, 2], name='encoder_conv1')
        conv1 = lrelu(conv1)
        conv2 = conv2d(conv1, 128, [4, 4], [2, 2], name='encoder_conv2')
        conv2 = lrelu(conv2)
        conv3 = conv2d(conv2, 256, [4, 4], [2, 2], name='encoder_conv3')
        conv3 = lrelu(conv3)
        conv3 = tf.reshape(conv3, [-1, np.prod(conv3.get_shape().as_list()[1:])])
        fc1 = dense(conv3, 512, name='encoder_fc1')
        fc1 = lrelu(fc1)
        self.mean = dense(fc1, latent_dim, name='encoder_mean')
        self.stddev = tf.nn.sigmoid(dense(fc1, latent_dim, name='encoder_stddev'))
        self.stddev = tf.maximum(self.stddev, 0.01)
        self.pred = self.mean + tf.multiply(self.stddev,
                                       tf.random_normal(tf.stack([tf.shape(X)[0], latent_dim])))

        if "elbo" in reg_type:
            self.reg_loss = tf.reduce_sum(-tf.log(self.stddev) + 0.5 * tf.square(self.stddev) +
                                           0.5 * tf.square(self.mean) - 0.5)
        elif "2norm" in reg_type:
            self.reg_loss = tf.reduce_sum(0.5 * tf.square(self.pred))
        elif "center" in reg_type:
            self.reg_loss = tf.reduce_sum(-tf.log(self.stddev) + 0.5 * tf.square(self.mean))
        elif "elbo0_1" in reg_type:
            self.reg_loss = 0.1 * tf.reduce_sum(-tf.log(self.stddev) + 0.5 * tf.square(self.stddev) +
                                           0.5 * tf.square(self.mean) - 0.5)
        elif "no_reg" in reg_type:
            self.reg_loss = 0.0 # Add something for stability
        elif "stein" in reg_type:
            stein_grad = tf.stop_gradient(self.tf_stein_gradient(self.pred, 1.0))
            self.reg_loss = -10000.0 * tf.reduce_sum(tf.multiply(self.pred, stein_grad))
        elif "adv" in reg_type:
            true_samples = tf.random_normal(tf.stack([tf.shape(X)[0], latent_dim]))
            self.d = mlp_discriminator(true_samples)
            self.d_ = mlp_discriminator(self.pred, reuse=True)

            epsilon = tf.random_uniform([], 0.0, 1.0)
            x_hat = epsilon * true_samples + (1 - epsilon) * self.pred
            d_hat = mlp_discriminator(x_hat, reuse=True)

            ddx = tf.gradients(d_hat, x_hat)[0]
            ddx = tf.sqrt(tf.reduce_sum(tf.square(ddx), axis=1))
            self.d_grad_loss = tf.reduce_mean(tf.square(ddx - 1.0) * 10.0)

            self.d_loss_x = -tf.reduce_mean(self.d)
            self.d_loss_e = tf.reduce_mean(self.d_)
            self.d_loss = self.d_loss_x + self.d_loss_e + self.d_grad_loss

            self.d_vars = [var for var in tf.global_variables() if 'd_net' in var.name]
            self.d_train = tf.train.AdamOptimizer(learning_rate=0.00002, beta1=0.5, beta2=0.9).minimize(self.d_loss,
                                                                                var_list=self.d_vars)
            tf.summary.scalar('d_loss_x', self.d_loss_x)
            tf.summary.scalar('d_loss_e', self.d_loss_e)
            self.reg_loss = -tf.reduce_mean(self.d_)
            self.reg_loss *= 100
        elif "moment" in reg_type:
            mean = tf.reduce_mean(self.pred, axis=0, keep_dims=True)
            var = tf.reduce_mean(tf.square(self.pred - mean), axis=0)
            mean_loss = tf.reduce_mean(tf.abs(mean))
            var_loss = tf.reduce_mean(tf.abs(var - 1.0))
            tf.summary.scalar('mean', mean_loss)
            tf.summary.scalar('variance', var_loss)
            self.reg_loss = mean_loss + var_loss
        elif "kernel" in reg_type:
            true_samples = tf.random_normal(tf.stack([200, latent_dim]))
            pred_kernel = self.compute_kernel(self.pred, self.pred)
            sample_kernel = self.compute_kernel(true_samples, true_samples)
            mix_kernel = self.compute_kernel(self.pred, true_samples)
            self.reg_loss = tf.reduce_mean(pred_kernel) + tf.reduce_mean(sample_kernel) - 2 * tf.reduce_mean(mix_kernel)
            self.reg_loss *= 100000.0
        else:
            print("Unknown regularization %s" % str(reg_type))
            exit(0)
        self.elbo_loss = tf.reduce_mean(-tf.log(self.stddev) + 0.5 * tf.square(self.stddev) +
                                        0.5 * tf.square(self.mean) - 0.5)

    def compute_kernel(self, x, y, sigma_sqr=1.0):
        x_size = tf.shape(x)[0]
        y_size = tf.shape(y)[0]
        dim = tf.shape(x)[1]
        tiled_x = tf.tile(tf.reshape(x, tf.stack([x_size, 1, dim])), tf.stack([1, y_size, 1]))
        tiled_y = tf.tile(tf.reshape(y, tf.stack([1, y_size, dim])), tf.stack([x_size, 1, 1]))
        kernel = tf.exp(-tf.reduce_mean(tf.square(tiled_x - tiled_y), axis=2) / 2.0 / sigma_sqr)
        return kernel

    # x_sample is input of size (batch_size, dim)
    def tf_stein_gradient(seff, x_sample, sigma_sqr):
        x_size = x_sample.get_shape()[0].value
        x_dim = x_sample.get_shape()[1].value
        x_sample = tf.reshape(x_sample, [x_size, 1, x_dim])
        sample_mat_y = tf.tile(x_sample, (1, x_size, 1))
        sample_mat_x = tf.transpose(sample_mat_y, perm=(1, 0, 2))
        kernel_matrix = tf.exp(-tf.reduce_sum(tf.square(sample_mat_x - sample_mat_y), axis=2) / (2 * sigma_sqr * x_dim))
        # np.multiply(-self.kernel(x, y), np.divide(x - y, self.sigma_sqr))./
        tiled_kernel = tf.tile(tf.reshape(kernel_matrix, [x_size, x_size, 1]), [1, 1, x_dim])
        kernel_grad_matrix = tf.multiply(tiled_kernel, tf.div(sample_mat_y - sample_mat_x, sigma_sqr * x_dim))
        gradient = tf.reshape(-x_sample, [x_size, 1, x_dim])  # Gradient of standard Gaussian
        tiled_gradient = tf.tile(gradient, [1, x_size, 1])
        weighted_gradient = tf.multiply(tiled_kernel, tiled_gradient)
        return tf.div(tf.reduce_sum(weighted_gradient, axis=0) +
                      tf.reduce_sum(kernel_grad_matrix, axis=1), x_size)
"""
class ComputeLL:
    def __init__(self, latent_dim):
        self.mean = tf.placeholder(tf.float32, shape=(None, latent_dim))
        self.stddev = tf.placeholder(tf.float32, shape=(None, latent_dim))
        self.sample = tf.placeholder(tf.float32, shape=(None, latent_dim))
        mu = tf.reshape(self.mean, shape=tf.pack([tf.shape(self.mean)[0], 1, latent_dim]))
        mu = tf.tile(mu, tf.pack([1, tf.shape(self.sample)[0], 1]))
        sig = tf.reshape(self.stddev, shape=tf.pack([tf.shape(self.stddev)[0], 1, latent_dim]))
        sig = tf.tile(sig, tf.pack([1, tf.shape(self.sample)[0], 1]))
        z = tf.reshape(self.sample, shape=tf.pack([1, tf.shape(self.sample)[0], latent_dim]))
        z = tf.tile(z, tf.pack([tf.shape(self.mean)[0], 1, 1]))

        coeff = tf.div(1.0 / math.sqrt(2 * math.pi), sig)
        ll = coeff * tf.exp(-tf.div(tf.square(z - mu), 2 * tf.square(sig)))
        ll = tf.reduce_prod(ll, axis=2)
        self.prob = ll

def compute_mutual_information(data, args, sess, encoder_list, ll_compute):
    print("Evaluating Mutual Information")
    start_time = time.time()
    num_batch = 1000
    z_batch_cnt = 10  # This must divide num_batch
    dist_batch_cnt = 10
    assert num_batch % z_batch_cnt == 0
    assert num_batch % dist_batch_cnt == 0
    batch_size = args.batch_size * args.nr_gpu

    sample_batches = np.zeros((num_batch*batch_size, args.latent_dim))
    mean_batches = np.zeros((num_batch*batch_size, args.latent_dim))
    stddev_batches = np.zeros((num_batch*batch_size, args.latent_dim))

    for batch in range(num_batch):
        x = data.next(args.batch_size * args.nr_gpu) # manually retrieve exactly init_batch_size examples
        x = np.split(x, args.nr_gpu)
        feed_dict = {encoder_list[i].x: x[i] for i in range(args.nr_gpu)}

        result = sess.run([encoder.pred for encoder in encoder_list] +
                          [encoder.mean for encoder in encoder_list] +
                          [encoder.stddev for encoder in encoder_list], feed_dict=feed_dict)
        sample = np.concatenate(result[0:args.nr_gpu], 0)
        z_mean = np.concatenate(result[args.nr_gpu:args.nr_gpu*2], 0)
        z_stddev = np.concatenate(result[args.nr_gpu*2:], 0)
        sample_batches[batch*batch_size:(batch+1)*batch_size, :] = sample
        mean_batches[batch*batch_size:(batch+1)*batch_size, :] = z_mean
        stddev_batches[batch*batch_size:(batch+1)*batch_size, :] = z_stddev

    z_batch_size = batch_size * z_batch_cnt
    dist_batch_size = batch_size * dist_batch_cnt
    prob_array = np.zeros((num_batch*batch_size, num_batch*batch_size), dtype=np.float)
    for z_ind in range(num_batch // z_batch_cnt):
        for dist_ind in range(num_batch // dist_batch_cnt):
            mean = mean_batches[dist_ind*dist_batch_size:(dist_ind+1)*dist_batch_size, :]
            stddev = stddev_batches[dist_ind*dist_batch_size:(dist_ind+1)*dist_batch_size, :]
            sample = sample_batches[z_ind*z_batch_size:(z_ind+1)*z_batch_size, :]
            probs = sess.run(ll_compute.prob, feed_dict={ll_compute.mean: mean,
                                                         ll_compute.stddev: stddev,
                                                         ll_compute.sample: sample})
            prob_array[dist_ind*dist_batch_size:(dist_ind+1)*dist_batch_size, z_ind*z_batch_size:(z_ind+1)*z_batch_size] = probs
        # print()
    # print(np.sum(prob_array))
    marginal = np.sum(prob_array, axis=0)
    ratio = np.log(np.divide(np.diagonal(prob_array), marginal)) + np.log(num_batch*batch_size)
    mutual_info = np.mean(ratio)
    print("Mutual Information %f, time elapsed %fs" % (mutual_info, time.time() - start_time))
    return mutual_info
"""


import sys

root=sys.argv[1]
output_dir=sys.argv[2]

from haojiepaint import *

import json
figsz = {
    'axes.labelsize': 12,
    'font.size': 12,
    'legend.fontsize': 12,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'figure.figsize': (6, 1.5),
}
plt.rcParams.update(figsz)
plt.rcParams['axes.titley'] = 1.0    # y is in axes-relative coordinates.
plt.rcParams['axes.titlepad'] = -12  # pad is in points..

def draw(ax, nnode, mbs):
    data_file = f'{root}/logs/fig11/perf_model_{nnode}nodes_mbs{mbs}.log'

    with open(data_file, 'r') as f:
        lines = f.readlines()
        naive_est = float(lines[0])
        data = json.loads(lines[1])
        x = data['est']
        y = data['real']

    v_min = min(min(x),min(y))
    v_max = max(max(x),max(y))
    v_min = int(v_min * 0.8) // 10 * 10
    v_max = int(v_max * 1.3) // 10 * 10

    # from sklearn.metrics import r2_score
    # print(nnode, mbs, r2_score(y,x))

    x = np.array(x)
    y = np.array(y)

    ax.scatter(x, y, marker=marker_def[-1], color=color_def[7])

    ax.plot(np.arange(v_min,v_max), np.arange(v_min,v_max), color=color_def[4], linestyle='--')
    ax.axvline(naive_est, color=color_def[18], linestyle='-')

    ax.set_xlim(v_min,v_max)
    ax.set_ylim(v_min,v_max)

    ax.set_title(f"{nnode*8}-{mbs}")

    if nnode == 1 and mbs == 8:
        ax.set_xticks([0,50,100])
        ax.set_yticks([0,50,100])
    if nnode == 2:
        ax.set_xticks([50,150,250])
        ax.set_yticks([50,150,250])

fig, axes = plt.subplots(1, 3)
fig.subplots_adjust(wspace=0.33)

draw(axes[0], 1, 4)
draw(axes[1], 1, 8)
draw(axes[2], 2, 8)
fig.supylabel('Real / ms')
fig.supxlabel('Estimation / ms',position=(0.5,-0.2))

plt.savefig(f'{output_dir}/fig11.pdf', bbox_inches='tight')


#input data- read files
rawdata = open("C:\codingstuff\day13input.txt")
datastring = rawdata.read()
rawdata.close()
testdataraw = open("C:\codingstuff\day13testinput.txt")
testdatastring = testdataraw.read()
testdataraw.close()

#---------what data am i testing----------
data= datastring

import numpy as np

#convert data into coordinates and instructions,
datasplit = data.split("\n\n")
coordinates_string = datasplit[0]
instructions_string = datasplit[1]
strings_of_the_coordinates = coordinates_string.split("\n")

#make tuples of all y,x pairs because numpy does y, x coordinates
coordinates= []
max_x = 0
max_y = 0
for d in strings_of_the_coordinates:
    x, y = list(map(int, d.split(',')))
    coordinates.append(( y , x ))
# create grid of all locations, based on maximum coordinates given
for c in coordinates:
    max_x = max(max_x, c[1])
    max_y = max(max_y, c[0],)
grid = np.zeros((max_y+1, max_x+1), dtype=int)
print("max y", max_y, "max x", max_x)
#populate the grid
for c in coordinates:
    grid[c[0], c[1]] = "1"

# make list of
#instructions= [["x", 655], ["y", 447], ["x", 327], ['y', 223],['x', 163], ['y' , 111],['x' ,81], ["y", 55], ['x' , 40], ["y", 27], ["y", 13], ['y' , 6]]
instructions= []
for instruction in instructions_string.split("\n"):
    instruct = [i for i in instruction if i.isdigit()  or i == "x" or i == "y"]
    instructions.append(instruct)
for id, instruction in enumerate(instructions):
        a= ""
        for i in instruction:
            if i.isdigit():
                a+= i
        instructions[id] = [instruction[0],int(a)]

print("instructions: ", instructions)

print(np.sum(grid))
# ---part 1 ------------------
def folder (grid, instructions, foldstoperform):
    counter= 0
    while counter < foldstoperform:
        for i in instructions:
            if i[0]== "y":
                for coordinate, value in np.ndenumerate(grid):
                    if coordinate[0] > i[1] and value == 1:
                        grid[coordinate[0] - 2*(coordinate[0] - i[1]), coordinate[1]] = 1
                        grid[coordinate] = 0
                counter += 1
                break
            if i[0] == "x":
                for coordinate, value in np.ndenumerate(grid):
                    if coordinate[1] > i[1]  and value == 1 :
                        grid[coordinate[0], coordinate[1]- 2*(coordinate[1] - i[1])] = 1
                        grid[coordinate] = 0
                counter += 1
                break
    #print(grid)
    print("visible dots:", np.sum(grid))

folder (grid, instructions, len(instructions)+1)


#
# Definition for binary tree:
# class Tree(object):
#   def __init__(self, x):
#     self.value = x
#     self.left = None
#     self.right = None
def isTreeSymmetric(t):
	if t == None: return True
	if left(t) != right(t): return False

	stack = [(t.left, t.right)]

	while len(stack) > 0:
		left_node, right_node = stack.pop()

		if left(left_node) != right(right_node) or right(left_node) != left(right_node): return False

		if left_node.left and right_node.right:
			stack.append((left_node.left, right_node.right))
		if left_node.right and right_node.left:
			stack.append((left_node.right, right_node.left))

	return True

def left(t):
	if t == None: return None
	if t.left == None: return None
	return t.left.value

def right(t):
	if t == None: return None
	if t.right == None: return None
	return t.right.value


# -*- coding: utf-8 -*-
from time import strftime, strptime

import scrapy

from scrapy_learn1v3.items import Mobilescoutcomments
from scrapy_learn1v3.utils.databaseUtil import MysqlUtil

class MobilescoutcommentsSpider(scrapy.Spider):
    "由于评论数不多，暂时不做增量更新了"
    "由于最多的评论数为9，暂时不做翻页"
    name = 'mobilescoutcomments'
    baseUrl = "https://www.mobilescout.com"

    def __init__(self):
        self.mysqlUtil = MysqlUtil()

    def start_requests(self):
        dataSet = self.mysqlUtil.select('mobilescout')
        time = 0
        for item in dataSet:
            # if time > 3:
            #     break
            # time += 1
            brand = item["brand"]
            title = item["title"]
            reply = item["reply"]
            if int(reply) > 0:
                # print("reply:", reply)
                comment_url = self.baseUrl + item["comment_url"]
                print("comment_url:", comment_url)
                yield scrapy.Request(url=comment_url, meta={"brand": brand, "title": title})
        pass

    def parse(self, response):
        brand = response.meta["brand"]
        title = response.meta["title"]

        lis = response.xpath(".//li[contains(@id,'post_')]")
        print("lis:", len(lis))
        for li in lis[1:]:
            post_time = li.xpath(".//span[@class='date']").xpath("string()").extract_first()
            print("post_time_before:", post_time)
            post_time = strftime("%Y-%m-%d %H:%M", strptime(post_time, "%m-%d-%Y, %I:%M %p")) if post_time else ""

            author = li.xpath(".//a[@class='username offline popupctrl']").xpath("string()").extract_first()
            author_title = li.xpath(".//span[@class='usertitle']").xpath("string()").extract_first()
            author_title = author_title.strip() if author_title else ""
            join_time = li.xpath(".//dl[@class='userinfo_extra']/dd/text()").extract_first()
            join_time = strftime("%Y-%m-%d", strptime(join_time, "%b %Y"
                                                                 "")) if join_time else ""

            posts = li.xpath(".//dl[@class='userinfo_extra']/dd[2]/text()").extract_first()
            posts = posts.replace(",", "") if posts else ""
            # content = li.xpath(".//div[@class='postrow']").xpath("string()").extract_first()
            content = li.xpath(".//div[contains(@id,'post_message')]").xpath("string()").extract_first()
            # content = li.xpath(".//div[contains(@id,'post_message')]/blockquote/text()").extract_first()
            content_extra = li.xpath(".//div[contains(@class,'quote_container')]").xpath("string()").extract_first()
            if content_extra:
                content = content.replace(content_extra, "")
            content = content.strip() if content else ""
            print("post_time:", post_time)
            print("author:", author)

            print("author_title:", author_title)
            print("join_time:", join_time)
            print("posts:", posts)
            print("content:", content)
            # print("content_extra:", content_extra)
            print("==========================================")

            yield Mobilescoutcomments(brand=brand, title=title, post_time=post_time, author=author,
                                      author_title=author_title, join_time=join_time,
                                      posts=posts, content=content)

        pass


# subdomain for your chat room (ie https://SUBDOMAIN.campfirenow.com)
subdomain = ''

# your Campfire API token (can be found under "My info" link in Campfire)
token = ''

# the name of the Campfire room you want to follow
room_name = ''

# a list of strings you want to watch for. this is a case insensitive search
watch_for = []

# how often (in seconds) you want to pull Campfire messages
check_interval = 5


import  pandas as pd
import numpy as np

info = pd.DataFrame(
    {
        "a_data":[45,28,39,32,18],
        "b_data":[22,19,11,25,16],
        "c_data":[22,19,11,25,16]
    }
)
print("The given Info is ")
print(info)
print("After The Use Of The shift()")
print(info.shift(periods=2, fill_value=0,axis=1))


import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util, template

class DashboardHandler(webapp.RequestHandler):
    def get(self, url=None):
        template_values = {
          'title' : 'Admin Dashboard'
        }
        path = os.path.join(os.path.dirname(__file__), '../', 'template', 'secondary.html')
        self.response.out.write(template.render(path, template_values))

def main():
    application = webapp.WSGIApplication([('/admin', DashboardHandler)],
                                         debug=True)
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()


__all__ = [
    "get_spindices",
    "spindices_to_ndarray",
    "BurstIndex_Royer_2012",
    "select_burst_spikes"
]

import numpy as np
import pandas as pd
from typing import Union

def get_spindices(data: np.ndarray) -> pd.DataFrame():
    """
    Get spike timestamps and spike id for each spike train in a
        sorted dataframe of spike trains
    Parameters
    ----------
    data : np.ndarray
        spike times for each spike train, in a list of arrays
    Returns
    -------
    spikes : pd.DataFrame
        sorted dataframe of spike times and spike id

    """
    spikes_id = []
    for spk_i, spk in enumerate(data):
        spikes_id.append(spk_i * np.ones_like(spk))

    spikes = pd.DataFrame()
    spikes["spike_times"] = np.hstack(data)
    spikes["spike_id"] = np.hstack(spikes_id)
    spikes.sort_values("spike_times", inplace=True)
    return spikes

def spindices_to_ndarray(
    spikes: pd.DataFrame, spike_id: Union[list, np.ndarray, None] = None
) -> np.ndarray:
    """
    Convert spike times and spike id to a list of arrays
    Parameters
    ----------
    spikes : pd.DataFrame
        sorted dataframe of spike times and spike id
    spike_id: list or np.ndarray
        spike ids search for in the dataframe (important if spikes were restricted)
    Returns
    -------
    data : np.ndarray
        spike times for each spike train, in a list of arrays
    """
    if spike_id is None:
        spike_id = np.unique(spikes["spike_id"])
    data = []
    for spk_i in spike_id:
        data.append(spikes[spikes["spike_id"] == spk_i]["spike_times"].values)
    return data

def BurstIndex_Royer_2012(autocorrs):
    # calc burst index from royer 2012
    # burst_idx will range from -1 to 1
    # -1 being non-bursty and 1 being bursty

    # peak range 2 - 9 ms
    peak = autocorrs.loc[0.002:0.009].max()
    # baseline idx 40 - 50 ms
    baseline = autocorrs.loc[0.04:0.05].mean()

    burst_idx = []
    for p, b in zip(peak, baseline):
        if p > b:
            burst_idx.append((p - b) / p)
        elif p < b:
            burst_idx.append((p - b) / b)
        else:
            burst_idx.append(np.nan)
    return burst_idx

def select_burst_spikes(spikes, mode="bursts", isiBursts=0.006, isiSpikes=0.020):
    """
    select_burst_spikes - Discriminate bursts vs single spikes.
    adpated from: http://fmatoolbox.sourceforge.net/Contents/FMAToolbox/Analyses/SelectSpikes.html

    Input:
        spikes: list of spike times
        mode: either 'bursts' (default) or 'single'
        isiBursts: max inter-spike interval for bursts (default = 0.006)
        isiSpikes: min for single spikes (default = 0.020)
    Output:
        selected: a logical vector indicating for each spike whether it
                    matches the criterion
    """

    dt = np.diff(spikes)

    if mode == "bursts":
        b = dt < isiBursts
        # either next or previous isi < threshold
        selected = np.insert(b, 0, False, axis=0) | np.append(b, False)
    else:
        s = dt > isiSpikes
        # either next or previous isi > threshold
        selected = np.insert(s, 0, False, axis=0) & np.append(s, False)

    return selected


from PIL import Image
import numpy as np
import matplotlib
import os
framecount = 338       #number of frames to put in array
band = 450              #band number to select frame output
outputfile = "singleslice"          #enter name for output file
tiffarray = np.zeros((1024, 1344, framecount))            #convert raster tiff to arraybased on tiff dimensions
#print(tiffarray.size)

#for i in range(338):          #run through each file in directory, use path for data to be aquired

tiffarray[:, :, :] = 100000

temparray = np.zeros((1024, framecount))            #array of zeros
#print(temparray.shape)
#print(temparray.size)
temparray = tiffarray[:,0,:]
print(tiffarray[:,0,:].shape)

matplotlib.image.imsave('rotated_frames/0test.tiff', tiffarray[:,band,:])      #saves to same directory as script, C:/python/

np.savetxt('rotated_frames/array.txt', [tiffarray[500,band,:]], delimiter=',', fmt='%d')


import numpy as np
import rdflib
import re
from collections import defaultdict
import csv
import os

# Get the required entities from the rdf file
def get_entities(file_loc):
	g = rdflib.Graph()
	g.load(file_loc)

	# Query is missing bibliographic ID
	query = """SELECT ?listItem ?seqID ?author ?title ?keyword
			WHERE {
			?listItem j.2:itemContent ?record .
			?record j.7:references ?manifest .
			?manifest j.4:embodimentOf ?expression .

			?listItem j.5:hasSequenceIdentifier ?seqID .
			?manifest j.3:keyword ?keyword .

			OPTIONAL {
				?expression j.0:creator ?author .
				FILTER(datatype(?author) = xsd:string) .
			}
			OPTIONAL {
				?expression j.0:title ?title .
				FILTER(datatype(?title) = xsd:string) .
			}
			}"""

	results = g.query(query)

	print("====== RDF Query ======")
	print("The query returned " + str(len(results)) + " results")
	print()

	g.close()
	return results

# Gather all keyword entities from the rdf graph
def get_keywords(entities):
	result = dict()

	return [entity['keyword'].value for entity in entities]

# Creates a dictionary with entities as keys and the split keyword arrays as values
def clean_and_split(entities):
	result = dict()
	counts = [0, 0, 0, 0, 0, 0, 0, 0]
	for entity in entities:
		keywords = entity['keyword'].value
		keywords = keywords.strip()
		keywords = re.split("[;:]\n\s*", keywords)
		result[entity] = keywords
		if len(keywords) > 6:
			counts[7] += 1
		else:
			counts[len(keywords)] += 1

	print("====== clean_and_split result ======")
	for i in range(7):
		print("Number of entries with " + str(i) + " keywords: " + str(counts[i]))
	print("Number of entries with more than 6 keywords: " + str(counts[7]))
	print()

	return result

# Separates keywords into individual words and adds words to appropriate dictionary
def create_dicts(separated_keywords):
	data_count = 0
	dicts = [defaultdict(int) for x in range(5)]
	for keyword_group in separated_keywords:
		if len(keyword_group) != 5:
			continue
		data_count += 1
		for keyword in keyword_group:
			for num in range(5):
				words = keyword.split()
				for raw in words:
					word = clean_word(raw)
					if word in dicts[num]:
						dicts[num][word] = dicts[num][word] + 1
					else:
						dicts[num][word] = 1

	print("====== create_dicts result ======")
	print("Words from " + str(data_count) + " keyword entries were used to create the dictionaries")
	print()
	if print_dictionaries:
		print_dicts(dicts)

	return dicts

# Removes special characters from words in order to attempt to avoid their influence
def clean_word(word):
	res = word.strip("\",.()[]!?{};’")
	res = re.sub("\w?[’'‘]", "", res)
	return res

# Prints dictionaries
def print_dicts(dicts):
	for dic in dicts:
		for w in sorted(dic, key=dic.get, reverse=True):
			print(w, dic[w])
		print("==============")

# Sorts separated keywords into categories according to the dictionaries.
# Extra word are included to prevent data loss
def sort_words(dicts, keyword_dicts, entities):
	sorted = defaultdict()
	for entity in entities:
		keywords = keyword_dicts.get(entity)
		if len(keywords) >= 5:
			sorted[entity] = keywords
		else:
			last_placed = -1
			indices = []
			order = ["NULL", "NULL", "NULL", "NULL", "NULL"]
			for num in range(len(keywords)):
				# Reduce the potential fields the keyword could be in by eliminating possibilities
				# This relies on the assumption that the data is in the correct order, even if incomplete
				front_lim = max(num, last_placed + 1)
				back_lim = 5 - len(keywords) + num

				words = keywords[num].split()
				scores = []

				for dict_num in range(front_lim, back_lim + 1):
					score = 0
					for raw_word in words:
						word = clean_word(raw_word)
						if word in dicts[num]:
							score = score + dicts[dict_num][word]
					scores.append(score)
				if not len(scores) == 0:
					order[num + np.argmax(scores)] = re.sub("\s{2,}", ", ", keywords[num])  # get rid of whitespace here
					last_placed = num + np.argmax(scores)
			sorted[entity] = order
	return sorted

def to_csv(entities, sorted_dict):
	with open("output.csv", 'w') as output:
		wr = csv.writer(output)
		if os.stat("output.csv").st_size == 0:
			wr.writerow(
				["ID", "Autor", "Titel", "Form", "Handlungsort", "Protagonisten", "Handlung", "Themen", "Extras ->"])
		# seqID author title keywords
		for entity in entities:
			row = []

			if entity['seqID'] is not None:
				row.append(entity['seqID'].value)
			else:
				row.append("NULL")

			if entity['author'] is not None:
				row.append(entity['author'].value)
			else:
				row.append("NULL")

			if entity['title'] is not None:
				row.append(entity['title'].value)
			else:
				row.append("NULL")

			row.extend(sorted_dict[entity])
			wr.writerow(row)

#################
# Config values #
#################
print_dictionaries = False
file_location = "MA_MODEL.rdf"

if __name__ == '__main__':
	print()

	entities = get_entities(file_location)

	entities = sorted(entities, key=lambda row: row['seqID'].value)

	keyword_dicts = clean_and_split(entities)

	dictionaries = create_dicts(keyword_dicts.values())

	sorted_dict = sort_words(dictionaries, keyword_dicts, entities)

	to_csv(entities, sorted_dict)


import os.path
import string
import random

__basedir__ = os.path.abspath(os.path.dirname(__file__))
__random_str__ = string.ascii_letters + string.digits + string.ascii_uppercase
__key__ = ''.join(random.choice(__random_str__) for index in range(50))

DEBUG = True

BASE_DIRECTORY = __basedir__
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(__basedir__, 'storage.db')
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY = __key__


from django import forms
from django.contrib.auth.models import User
from .models import Discussion, DiscussionHead, DiscussionRecipient, DiscussionUserGroup

def must_be_empty(value):
    if value:
        raise forms.ValidationError('is not empty')

class DiscussionHeadForm(forms.Form):
    head_name = forms.CharField(required=True, widget=forms.TextInput(
        attrs={
            'class': 'form-control',
            'placeholder': 'Add new discussion head',
            'max-length': '255',
        }
    ))

    def clean(self):
        data = self.cleaned_data
        if data.get('head_name') == '':
            raise forms.ValidationError('Please write something before post.')

        if DiscussionHead.objects.filter(head_name=data.get('head_name')).exists():
            raise forms.ValidationError(
                "This head name already used. Try with new one."
            )
        data['head_name'] = data.get('head_name')

        return data

    class Meta:
        model = DiscussionHead
        fields = ['head_name']

class DiscussionForm(forms.Form):
    message = forms.CharField(required=True, widget=forms.TextInput(
        attrs={
            'class': 'write_msg',
            'max-length': '255',
            'placeholder': 'Type a message'
        }
    ))

    discussion_head_id = forms.CharField(required=True, widget=forms.HiddenInput)

    honeypot = forms.CharField(required=False, widget=forms.HiddenInput, label="Leave empty",
                               validators=[must_be_empty])

    def clean(self):
        data = self.cleaned_data
        data['message'] = data.get('message')
        data['discussion_head_id'] = data.get('discussion_head_id')
        return data

    class Meta:
        model = Discussion
        fields = ['message', 'discussion_head_id']


import os
from itertools import compress

import torch
import glob as gb
import numpy as np
import cv2
import csv
import pathlib
from shapely.geometry import Polygon

def get_images(root, limit=None):
    '''
    get images's path and name
    '''
    files = []
    for ext in ['jpg', 'png', 'jpeg', 'JPG']:
        files.extend(gb.glob(os.path.join(root, '*.{}'.format(ext))))
    name = []
    for i in range(len(files) if limit is None else limit):
        name.append(files[i].split('/')[-1])
    return files[:len(name)], name

def load_annoataion(p):
    '''
    load annotation from the text file
    :param p:
    :return:
    '''
    text_polys = []
    text_tags = []
    texts = []
    if not os.path.exists(p):
        return np.array(text_polys, dtype=np.float32)
    with open(p, 'r') as f:
        reader = csv.reader(f)
        for line in reader:
            label = line[-1].strip()
            # strip BOM. \ufeff for python3,  \xef\xbb\bf for python2
            line = [i.strip('\ufeff').strip('\xef\xbb\xbf') for i in line]

            x1, y1, x2, y2, x3, y3, x4, y4 = list(map(float, line[:8]))
            text_polys.append([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
            texts.append(label)
            if label == '*' or label == '###' or label == '':
                text_tags.append(True)
            else:
                text_tags.append(False)
        return np.array(text_polys, dtype=np.float32), np.array(texts), np.array(text_tags, dtype=np.bool)

def polygon_area(poly):
    '''
    compute area of a polygon
    :param poly:
    :return:
    '''
    edge = [
        (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]),
        (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]),
        (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]),
        (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1])
    ]
    return np.sum(edge) / 2.

def check_and_validate_polys(polys, texts, tags, xxx_todo_changeme):
    '''
    check so that the text poly is in the same direction,
    and also filter some invalid polygons
    :param polys:   all the text area poly
    :param texts:    all the transcripts
    :param tags:    all the text tags
    :return:    validated_polys,validated_text,validated_text
    '''
    (h, w) = xxx_todo_changeme
    if polys.shape[0] == 0:
        return polys
    polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1)
    polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1)

    validated_polys = []
    validated_text = []
    validated_tags = []
    for poly, text, tag in zip(polys, texts, tags):
        p_area = polygon_area(poly)
        if abs(p_area) < 1:
            # print poly
            print('invalid poly')
            continue
        if p_area > 0:
            print('poly in wrong direction')
            poly = poly[(0, 3, 2, 1), :]
        validated_polys.append(poly)
        validated_text.append(text)
        validated_tags.append(tag)
    return np.array(validated_polys), np.array(validated_text), np.array(validated_tags)

def crop_area(im, polys, tags, texts, crop_background=False, max_tries=50):
    """
    make random crop from the input image
    """
    h, w, _ = im.shape
    pad_h = h // 10
    pad_w = w // 10
    h_array = np.zeros((h + pad_h * 2), dtype=np.int32)
    w_array = np.zeros((w + pad_w * 2), dtype=np.int32)
    for poly in polys:
        poly = np.round(poly, decimals=0).astype(np.int32)
        minx = np.min(poly[:, 0])
        maxx = np.max(poly[:, 0])
        w_array[minx + pad_w:maxx + pad_w] = 1
        miny = np.min(poly[:, 1])
        maxy = np.max(poly[:, 1])
        h_array[miny + pad_h:maxy + pad_h] = 1
    # ensure the cropped area not across a text
    h_axis = np.where(h_array == 0)[0]
    w_axis = np.where(w_array == 0)[0]
    if len(h_axis) == 0 or len(w_axis) == 0:
        return im, polys, tags, np.array(len(polys))
    for i in range(max_tries):
        xx = np.random.choice(w_axis, size=2)
        xmin = np.min(xx) - pad_w
        xmax = np.max(xx) - pad_w
        xmin = np.clip(xmin, 0, w - 1)
        xmax = np.clip(xmax, 0, w - 1)
        yy = np.random.choice(h_axis, size=2)
        ymin = np.min(yy) - pad_h
        ymax = np.max(yy) - pad_h
        ymin = np.clip(ymin, 0, h - 1)
        ymax = np.clip(ymax, 0, h - 1)
        # if xmax - xmin < FLAGS.min_crop_side_ratio*w or ymax - ymin < FLAGS.min_crop_side_ratio*h:
        if xmax - xmin < 0.1 * w or ymax - ymin < 0.1 * h:
            # area too small
            continue
        if polys.shape[0] != 0:
            poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \
                                & (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax)
            selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0]
        else:
            selected_polys = []
        if len(selected_polys) == 0:
            # no text in this area
            if crop_background:
                return im[ymin:ymax + 1, xmin:xmax + 1, :], polys[selected_polys], tags[selected_polys], []
            else:
                continue
        im = im[ymin:ymax + 1, xmin:xmax + 1, :]
        polys = polys[selected_polys]
        tags = tags[selected_polys]
        texts = texts[selected_polys]
        polys[:, :, 0] -= xmin
        polys[:, :, 1] -= ymin
        return im, polys, tags, texts, selected_polys

    return im, polys, tags, texts, np.array(len(polys))

def shrink_poly(poly, r):
    '''
    fit a poly inside the origin poly, maybe bugs here...
    used for generate the score map
    :param poly: the text poly
    :param r: r in the paper
    :return: the shrinked poly
    '''
    # shrink ratio
    R = 0.3
    # find the longer pair
    if np.linalg.norm(poly[0] - poly[1]) + np.linalg.norm(poly[2] - poly[3]) > \
            np.linalg.norm(poly[0] - poly[3]) + np.linalg.norm(poly[1] - poly[2]):
        # first move (p0, p1), (p2, p3), then (p0, p3), (p1, p2)
        ## p0, p1
        theta = np.arctan2((poly[1][1] - poly[0][1]), (poly[1][0] - poly[0][0]))
        poly[0][0] += R * r[0] * np.cos(theta)
        poly[0][1] += R * r[0] * np.sin(theta)
        poly[1][0] -= R * r[1] * np.cos(theta)
        poly[1][1] -= R * r[1] * np.sin(theta)
        ## p2, p3
        theta = np.arctan2((poly[2][1] - poly[3][1]), (poly[2][0] - poly[3][0]))
        poly[3][0] += R * r[3] * np.cos(theta)
        poly[3][1] += R * r[3] * np.sin(theta)
        poly[2][0] -= R * r[2] * np.cos(theta)
        poly[2][1] -= R * r[2] * np.sin(theta)
        ## p0, p3
        theta = np.arctan2((poly[3][0] - poly[0][0]), (poly[3][1] - poly[0][1]))
        poly[0][0] += R * r[0] * np.sin(theta)
        poly[0][1] += R * r[0] * np.cos(theta)
        poly[3][0] -= R * r[3] * np.sin(theta)
        poly[3][1] -= R * r[3] * np.cos(theta)
        ## p1, p2
        theta = np.arctan2((poly[2][0] - poly[1][0]), (poly[2][1] - poly[1][1]))
        poly[1][0] += R * r[1] * np.sin(theta)
        poly[1][1] += R * r[1] * np.cos(theta)
        poly[2][0] -= R * r[2] * np.sin(theta)
        poly[2][1] -= R * r[2] * np.cos(theta)
    else:
        ## p0, p3
        # print poly
        theta = np.arctan2((poly[3][0] - poly[0][0]), (poly[3][1] - poly[0][1]))
        poly[0][0] += R * r[0] * np.sin(theta)
        poly[0][1] += R * r[0] * np.cos(theta)
        poly[3][0] -= R * r[3] * np.sin(theta)
        poly[3][1] -= R * r[3] * np.cos(theta)
        ## p1, p2
        theta = np.arctan2((poly[2][0] - poly[1][0]), (poly[2][1] - poly[1][1]))
        poly[1][0] += R * r[1] * np.sin(theta)
        poly[1][1] += R * r[1] * np.cos(theta)
        poly[2][0] -= R * r[2] * np.sin(theta)
        poly[2][1] -= R * r[2] * np.cos(theta)
        ## p0, p1
        theta = np.arctan2((poly[1][1] - poly[0][1]), (poly[1][0] - poly[0][0]))
        poly[0][0] += R * r[0] * np.cos(theta)
        poly[0][1] += R * r[0] * np.sin(theta)
        poly[1][0] -= R * r[1] * np.cos(theta)
        poly[1][1] -= R * r[1] * np.sin(theta)
        ## p2, p3
        theta = np.arctan2((poly[2][1] - poly[3][1]), (poly[2][0] - poly[3][0]))
        poly[3][0] += R * r[3] * np.cos(theta)
        poly[3][1] += R * r[3] * np.sin(theta)
        poly[2][0] -= R * r[2] * np.cos(theta)
        poly[2][1] -= R * r[2] * np.sin(theta)
    return poly

def point_dist_to_line(p1, p2, p3):
    # compute the distance from p3 to p1-p2
    return np.linalg.norm(np.cross(p2 - p1, p1 - p3)) / np.linalg.norm(p2 - p1)

def fit_line(p1, p2):
    # fit a line ax+by+c = 0
    if p1[0] == p1[1]:
        return [1., 0., -p1[0]]
    else:
        [k, b] = np.polyfit(p1, p2, deg=1)
        return [k, -1., b]

def line_cross_point(line1, line2):
    # line1 0= ax+by+c, compute the cross point of line1 and line2
    if line1[0] != 0 and line1[0] == line2[0]:
        print('Cross point does not exist')
        return None
    if line1[0] == 0 and line2[0] == 0:
        print('Cross point does not exist')
        return None
    if line1[1] == 0:
        x = -line1[2]
        y = line2[0] * x + line2[2]
    elif line2[1] == 0:
        x = -line2[2]
        y = line1[0] * x + line1[2]
    else:
        k1, _, b1 = line1
        k2, _, b2 = line2
        x = -(b1 - b2) / (k1 - k2)
        y = k1 * x + b1
    return np.array([x, y], dtype=np.float32)

def line_verticle(line, point):
    # get the verticle line from line across point
    if line[1] == 0:
        verticle = [0, -1, point[1]]
    else:
        if line[0] == 0:
            verticle = [1, 0, -point[0]]
        else:
            verticle = [-1. / line[0], -1, point[1] - (-1 / line[0] * point[0])]
    return verticle

def rectangle_from_parallelogram(poly):
    '''
    fit a rectangle from a parallelogram
    :param poly:
    :return:
    '''
    p0, p1, p2, p3 = poly
    angle_p0 = np.arccos(np.dot(p1 - p0, p3 - p0) / (np.linalg.norm(p0 - p1) * np.linalg.norm(p3 - p0)))
    if angle_p0 < 0.5 * np.pi:
        if np.linalg.norm(p0 - p1) > np.linalg.norm(p0 - p3):
            # p0 and p2
            ## p0
            p2p3 = fit_line([p2[0], p3[0]], [p2[1], p3[1]])
            p2p3_verticle = line_verticle(p2p3, p0)

            new_p3 = line_cross_point(p2p3, p2p3_verticle)
            ## p2
            p0p1 = fit_line([p0[0], p1[0]], [p0[1], p1[1]])
            p0p1_verticle = line_verticle(p0p1, p2)

            new_p1 = line_cross_point(p0p1, p0p1_verticle)
            return np.array([p0, new_p1, p2, new_p3], dtype=np.float32)
        else:
            p1p2 = fit_line([p1[0], p2[0]], [p1[1], p2[1]])
            p1p2_verticle = line_verticle(p1p2, p0)

            new_p1 = line_cross_point(p1p2, p1p2_verticle)
            p0p3 = fit_line([p0[0], p3[0]], [p0[1], p3[1]])
            p0p3_verticle = line_verticle(p0p3, p2)

            new_p3 = line_cross_point(p0p3, p0p3_verticle)
            return np.array([p0, new_p1, p2, new_p3], dtype=np.float32)
    else:
        if np.linalg.norm(p0 - p1) > np.linalg.norm(p0 - p3):
            # p1 and p3
            ## p1
            p2p3 = fit_line([p2[0], p3[0]], [p2[1], p3[1]])
            p2p3_verticle = line_verticle(p2p3, p1)

            new_p2 = line_cross_point(p2p3, p2p3_verticle)
            ## p3
            p0p1 = fit_line([p0[0], p1[0]], [p0[1], p1[1]])
            p0p1_verticle = line_verticle(p0p1, p3)

            new_p0 = line_cross_point(p0p1, p0p1_verticle)
            return np.array([new_p0, p1, new_p2, p3], dtype=np.float32)
        else:
            p0p3 = fit_line([p0[0], p3[0]], [p0[1], p3[1]])
            p0p3_verticle = line_verticle(p0p3, p1)

            new_p0 = line_cross_point(p0p3, p0p3_verticle)
            p1p2 = fit_line([p1[0], p2[0]], [p1[1], p2[1]])
            p1p2_verticle = line_verticle(p1p2, p3)

            new_p2 = line_cross_point(p1p2, p1p2_verticle)
            return np.array([new_p0, p1, new_p2, p3], dtype=np.float32)

def sort_rectangle(poly):
    # sort the four coordinates of the polygon, points in poly should be sorted clockwise
    # First find the lowest point
    p_lowest = np.argmax(poly[:, 1])
    if np.count_nonzero(poly[:, 1] == poly[p_lowest, 1]) == 2:
        # 底边平行于X轴, 那么p0为左上角
        p0_index = np.argmin(np.sum(poly, axis=1))
        p1_index = (p0_index + 1) % 4
        p2_index = (p0_index + 2) % 4
        p3_index = (p0_index + 3) % 4
        return poly[[p0_index, p1_index, p2_index, p3_index]], 0.
    else:
        # 找到最低点右边的点
        p_lowest_right = (p_lowest - 1) % 4
        p_lowest_left = (p_lowest + 1) % 4
        angle = np.arctan(
            -(poly[p_lowest][1] - poly[p_lowest_right][1]) / (poly[p_lowest][0] - poly[p_lowest_right][0]))
        # assert angle > 0
        if angle <= 0:
            print(angle, poly[p_lowest], poly[p_lowest_right])
        if angle / np.pi * 180 > 45:
            # 这个点为p2
            p2_index = p_lowest
            p1_index = (p2_index - 1) % 4
            p0_index = (p2_index - 2) % 4
            p3_index = (p2_index + 1) % 4
            return poly[[p0_index, p1_index, p2_index, p3_index]], -(np.pi / 2 - angle)
        else:
            # 这个点为p3
            p3_index = p_lowest
            p0_index = (p3_index + 1) % 4
            p1_index = (p3_index + 2) % 4
            p2_index = (p3_index + 3) % 4
            return poly[[p0_index, p1_index, p2_index, p3_index]], angle

def restore_rectangle_rbox(origin, geometry):
    d = geometry[:, :4]
    angle = geometry[:, 4]
    # for angle > 0
    origin_0 = origin[angle >= 0]
    d_0 = d[angle >= 0]
    angle_0 = angle[angle >= 0]
    if origin_0.shape[0] > 0:
        p = np.array([np.zeros(d_0.shape[0]), -d_0[:, 0] - d_0[:, 2],
                      d_0[:, 1] + d_0[:, 3], -d_0[:, 0] - d_0[:, 2],
                      d_0[:, 1] + d_0[:, 3], np.zeros(d_0.shape[0]),
                      np.zeros(d_0.shape[0]), np.zeros(d_0.shape[0]),
                      d_0[:, 3], -d_0[:, 2]])
        p = p.transpose((1, 0)).reshape((-1, 5, 2))  # N*5*2

        rotate_matrix_x = np.array([np.cos(angle_0), np.sin(angle_0)]).transpose((1, 0))
        rotate_matrix_x = np.repeat(rotate_matrix_x, 5, axis=1).reshape(-1, 2, 5).transpose((0, 2, 1))  # N*5*2

        rotate_matrix_y = np.array([-np.sin(angle_0), np.cos(angle_0)]).transpose((1, 0))
        rotate_matrix_y = np.repeat(rotate_matrix_y, 5, axis=1).reshape(-1, 2, 5).transpose((0, 2, 1))

        p_rotate_x = np.sum(rotate_matrix_x * p, axis=2)[:, :, np.newaxis]  # N*5*1
        p_rotate_y = np.sum(rotate_matrix_y * p, axis=2)[:, :, np.newaxis]  # N*5*1

        p_rotate = np.concatenate([p_rotate_x, p_rotate_y], axis=2)  # N*5*2

        p3_in_origin = origin_0 - p_rotate[:, 4, :]
        new_p0 = p_rotate[:, 0, :] + p3_in_origin  # N*2
        new_p1 = p_rotate[:, 1, :] + p3_in_origin
        new_p2 = p_rotate[:, 2, :] + p3_in_origin
        new_p3 = p_rotate[:, 3, :] + p3_in_origin

        new_p_0 = np.concatenate([new_p0[:, np.newaxis, :], new_p1[:, np.newaxis, :],
                                  new_p2[:, np.newaxis, :], new_p3[:, np.newaxis, :]], axis=1)  # N*4*2
    else:
        new_p_0 = np.zeros((0, 4, 2))
    # for angle < 0
    origin_1 = origin[angle < 0]
    d_1 = d[angle < 0]
    angle_1 = angle[angle < 0]
    if origin_1.shape[0] > 0:
        p = np.array([-d_1[:, 1] - d_1[:, 3], -d_1[:, 0] - d_1[:, 2],
                      np.zeros(d_1.shape[0]), -d_1[:, 0] - d_1[:, 2],
                      np.zeros(d_1.shape[0]), np.zeros(d_1.shape[0]),
                      -d_1[:, 1] - d_1[:, 3], np.zeros(d_1.shape[0]),
                      -d_1[:, 1], -d_1[:, 2]])
        p = p.transpose((1, 0)).reshape((-1, 5, 2))  # N*5*2

        rotate_matrix_x = np.array([np.cos(-angle_1), -np.sin(-angle_1)]).transpose((1, 0))
        rotate_matrix_x = np.repeat(rotate_matrix_x, 5, axis=1).reshape(-1, 2, 5).transpose((0, 2, 1))  # N*5*2

        rotate_matrix_y = np.array([np.sin(-angle_1), np.cos(-angle_1)]).transpose((1, 0))
        rotate_matrix_y = np.repeat(rotate_matrix_y, 5, axis=1).reshape(-1, 2, 5).transpose((0, 2, 1))

        p_rotate_x = np.sum(rotate_matrix_x * p, axis=2)[:, :, np.newaxis]  # N*5*1
        p_rotate_y = np.sum(rotate_matrix_y * p, axis=2)[:, :, np.newaxis]  # N*5*1

        p_rotate = np.concatenate([p_rotate_x, p_rotate_y], axis=2)  # N*5*2

        p3_in_origin = origin_1 - p_rotate[:, 4, :]
        new_p0 = p_rotate[:, 0, :] + p3_in_origin  # N*2
        new_p1 = p_rotate[:, 1, :] + p3_in_origin
        new_p2 = p_rotate[:, 2, :] + p3_in_origin
        new_p3 = p_rotate[:, 3, :] + p3_in_origin

        new_p_1 = np.concatenate([new_p0[:, np.newaxis, :], new_p1[:, np.newaxis, :],
                                  new_p2[:, np.newaxis, :], new_p3[:, np.newaxis, :]], axis=1)  # N*4*2
    else:
        new_p_1 = np.zeros((0, 4, 2))
    return np.concatenate([new_p_0, new_p_1])

def restore_rectangle(origin, geometry):
    return restore_rectangle_rbox(origin, geometry)

def generate_rbox(im_size, polys, tags):
    h, w = im_size
    poly_mask = np.zeros((h, w), dtype=np.uint8)
    score_map = np.zeros((h, w), dtype=np.uint8)
    geo_map = np.zeros((h, w, 5), dtype=np.float32)
    # mask used during traning, to ignore some hard areas
    training_mask = np.ones((h, w), dtype=np.uint8)
    rectangles = []

    for poly_idx, poly_tag in enumerate(zip(polys, tags)):
        poly = poly_tag[0]
        tag = poly_tag[1]

        r = [None, None, None, None]
        for i in range(4):
            r[i] = min(np.linalg.norm(poly[i] - poly[(i + 1) % 4]),
                       np.linalg.norm(poly[i] - poly[(i - 1) % 4]))
        # score map
        shrinked_poly = shrink_poly(poly.copy(), r).astype(np.int32)[np.newaxis, :, :]
        cv2.fillPoly(score_map, shrinked_poly, 1)
        cv2.fillPoly(poly_mask, shrinked_poly, poly_idx + 1)
        # if the poly is too small, then ignore it during training
        poly_h = min(np.linalg.norm(poly[0] - poly[3]), np.linalg.norm(poly[1] - poly[2]))
        poly_w = min(np.linalg.norm(poly[0] - poly[1]), np.linalg.norm(poly[2] - poly[3]))
        # if min(poly_h, poly_w) < FLAGS.min_text_size:
        if min(poly_h, poly_w) < 10:
            cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0)
        if tag:
            cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0)

        xy_in_poly = np.argwhere(poly_mask == (poly_idx + 1))
        fitted_parallelograms = []
        for i in range(4):
            p0 = poly[i]
            p1 = poly[(i + 1) % 4]
            p2 = poly[(i + 2) % 4]
            p3 = poly[(i + 3) % 4]
            edge = fit_line([p0[0], p1[0]], [p0[1], p1[1]])
            backward_edge = fit_line([p0[0], p3[0]], [p0[1], p3[1]])
            forward_edge = fit_line([p1[0], p2[0]], [p1[1], p2[1]])
            if point_dist_to_line(p0, p1, p2) > point_dist_to_line(p0, p1, p3):
                # 平行线经过p2
                if edge[1] == 0:
                    edge_opposite = [1, 0, -p2[0]]
                else:
                    edge_opposite = [edge[0], -1, p2[1] - edge[0] * p2[0]]
            else:
                # 经过p3
                if edge[1] == 0:
                    edge_opposite = [1, 0, -p3[0]]
                else:
                    edge_opposite = [edge[0], -1, p3[1] - edge[0] * p3[0]]
            # move forward edge
            new_p0 = p0
            new_p1 = p1
            new_p2 = p2
            new_p3 = p3
            new_p2 = line_cross_point(forward_edge, edge_opposite)
            if point_dist_to_line(p1, new_p2, p0) > point_dist_to_line(p1, new_p2, p3):
                # across p0
                if forward_edge[1] == 0:
                    forward_opposite = [1, 0, -p0[0]]
                else:
                    forward_opposite = [forward_edge[0], -1, p0[1] - forward_edge[0] * p0[0]]
            else:
                # across p3
                if forward_edge[1] == 0:
                    forward_opposite = [1, 0, -p3[0]]
                else:
                    forward_opposite = [forward_edge[0], -1, p3[1] - forward_edge[0] * p3[0]]
            new_p0 = line_cross_point(forward_opposite, edge)
            new_p3 = line_cross_point(forward_opposite, edge_opposite)
            fitted_parallelograms.append([new_p0, new_p1, new_p2, new_p3, new_p0])
            # or move backward edge
            new_p0 = p0
            new_p1 = p1
            new_p2 = p2
            new_p3 = p3
            new_p3 = line_cross_point(backward_edge, edge_opposite)
            if point_dist_to_line(p0, p3, p1) > point_dist_to_line(p0, p3, p2):
                # across p1
                if backward_edge[1] == 0:
                    backward_opposite = [1, 0, -p1[0]]
                else:
                    backward_opposite = [backward_edge[0], -1, p1[1] - backward_edge[0] * p1[0]]
            else:
                # across p2
                if backward_edge[1] == 0:
                    backward_opposite = [1, 0, -p2[0]]
                else:
                    backward_opposite = [backward_edge[0], -1, p2[1] - backward_edge[0] * p2[0]]
            new_p1 = line_cross_point(backward_opposite, edge)
            new_p2 = line_cross_point(backward_opposite, edge_opposite)
            fitted_parallelograms.append([new_p0, new_p1, new_p2, new_p3, new_p0])
        areas = [Polygon(t).area for t in fitted_parallelograms]
        parallelogram = np.array(fitted_parallelograms[np.argmin(areas)][:-1], dtype=np.float32)
        # sort thie polygon
        parallelogram_coord_sum = np.sum(parallelogram, axis=1)
        min_coord_idx = np.argmin(parallelogram_coord_sum)
        parallelogram = parallelogram[
            [min_coord_idx, (min_coord_idx + 1) % 4, (min_coord_idx + 2) % 4, (min_coord_idx + 3) % 4]]

        rectangle = rectangle_from_parallelogram(parallelogram)
        rectangle, rotate_angle = sort_rectangle(rectangle)
        rectangles.append(rectangle.flatten())

        p0_rect, p1_rect, p2_rect, p3_rect = rectangle
        for y, x in xy_in_poly:
            point = np.array([x, y], dtype=np.float32)
            # top
            geo_map[y, x, 0] = point_dist_to_line(p0_rect, p1_rect, point)
            # right
            geo_map[y, x, 1] = point_dist_to_line(p1_rect, p2_rect, point)
            # down
            geo_map[y, x, 2] = point_dist_to_line(p2_rect, p3_rect, point)
            # left
            geo_map[y, x, 3] = point_dist_to_line(p3_rect, p0_rect, point)
            # angle
            geo_map[y, x, 4] = rotate_angle
    return score_map, geo_map, training_mask, rectangles

def rotate_image_and_points(image, points, angle):
    # center pivot
    image_center = tuple(np.array(image.shape[1::-1]) / 2)
    rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
    rotated_img = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR,
                                 borderMode=cv2.BORDER_REPLICATE)
    points_ones = np.hstack([points, np.ones(shape=(len(points), 1))])
    rotated_pts = rot_mat.dot(points_ones.T).T
    return rotated_img, rotated_pts

def image_label(txt_root, image_list, img_name, index,
                input_size=512, random_scale=np.array([0.5, 1, 2.0, 3.0]),
                background_ratio=3. / 8,
                random_rotate_degree=np.arange(-15, 16, 2),
                ):
    """
    get image's corresponding matrix and ground truth
    如果预测样张较小的话，input_size需要适当调小，且是128的整数倍数
    """

    try:
        image_filename = image_list[index]
        cur_img_name = img_name[index]
        cur_img = cv2.imread(image_filename)
        h, w, _ = cur_img.shape

        gt_file_name = 'gt_' + cur_img_name.replace(cur_img_name.split('.')[1], 'txt')
        gt_file_name = os.path.join(txt_root, gt_file_name)
        # 加载并过滤掉不合适的多边形与文本
        text_polys, texts, text_tags = load_annoataion(gt_file_name)  # text_polys: n * 4 * 2
        text_polys, texts, text_tags = check_and_validate_polys(text_polys, texts, text_tags, (h, w))
        # 选择随机的缩放比例
        rd_scale = np.random.choice(random_scale)
        rd_rotate_degree = np.random.choice(random_rotate_degree)
        # 将当前图像缩放到随机的缩放比例
        cur_img = cv2.resize(cur_img, dsize=None, fx=rd_scale, fy=rd_scale)
        text_polys *= rd_scale
        cur_img, text_polys = rotate_image_and_points(cur_img, np.reshape(text_polys, (-1, 2)), rd_rotate_degree)
        text_polys = np.reshape(text_polys, (-1, 4, 2))
        rectangles = []

        # 一定概率概率随机裁剪背景还是有多边形的区域
        if np.random.rand() < background_ratio:
            # crop background
            cur_img, text_polys, text_tags,texts, selected_poly = crop_area(cur_img, text_polys, text_tags,texts, crop_background=True)
            assert len(text_polys) == 0, 'Background crop error'
            new_h, new_w, _ = cur_img.shape
            max_h_w_i = np.max([new_h, new_w, input_size])
            im_padded = np.zeros((max_h_w_i, max_h_w_i, 3), dtype=np.uint8)
            im_padded[:new_h, :new_w, :] = cur_img.copy()
            cur_img = cv2.resize(im_padded, dsize=(input_size, input_size))
            score_map = np.zeros((input_size, input_size), dtype=np.uint8)
            geo_map_channels = 5
            geo_map = np.zeros((input_size, input_size, geo_map_channels), dtype=np.float32)
            training_mask = np.ones((input_size, input_size), dtype=np.uint8)
        else:
            cur_img, text_polys, text_tags, texts, selected_poly = crop_area(cur_img, text_polys, text_tags,texts, crop_background=False)
            assert len(text_polys) > 0, 'Text area crop error'
            h, w, _ = cur_img.shape

            new_h, new_w, _ = cur_img.shape
            # 图像左上角pad填充
            max_h_w_i = np.max([new_h, new_w, input_size])
            im_padded = np.zeros((max_h_w_i, max_h_w_i, 3), dtype=np.uint8)
            im_padded[:new_h, :new_w, :] = cur_img.copy()
            cur_img = im_padded
            # 将填充后的图像resize到目标大小
            new_h, new_w, _ = cur_img.shape
            resize_h = input_size
            resize_w = input_size
            cur_img = cv2.resize(cur_img, dsize=(resize_w, resize_h))
            resize_ratio_3_x = resize_w / float(new_w)
            resize_ratio_3_y = resize_h / float(new_h)
            text_polys[:, :, 0] *= resize_ratio_3_x
            text_polys[:, :, 1] *= resize_ratio_3_y
            new_h, new_w, _ = cur_img.shape
            score_map, geo_map, training_mask, rectangles = generate_rbox((new_h, new_w), text_polys, text_tags)
        assert len(rectangles) == len(texts), "number of box and text don't match"
        step_size = int(input_size // 128)
        images = cur_img[:, :, ::-1].astype(np.float32)
        score_maps = score_map[::step_size, ::step_size, np.newaxis].astype(np.float32)
        geo_maps = geo_map[::step_size, ::step_size, :].astype(np.float32)
        training_masks = training_mask[::step_size, ::step_size, np.newaxis].astype(np.float32)

        texts = list(compress(texts, 1-text_tags))
        rectangles = list(compress(rectangles, 1-text_tags))

        assert len(texts) == len(rectangles) and len(texts) > 0

    except Exception as e:
        cur_img_name, images, score_maps, geo_maps, training_masks, texts, rectangles = None, None, None, None, None, None, None

    return cur_img_name, images, score_maps, geo_maps, training_masks, texts, rectangles

# def collate_fn(batch):
#     img, score_map, geo_map, training_mask = zip(*batch)
#     bs = len(score_map)
#     images = []
#     score_maps = []
#     geo_maps = []
#     training_masks = []
#     for i in range(bs):
#         if img[i] is not None:
#             a = torch.from_numpy(img[i])
#             a = a.permute(2, 0, 1)
#             images.append(a)
#             b = torch.from_numpy(score_map[i])
#             b = b.permute(2, 0, 1)
#             score_maps.append(b)
#             c = torch.from_numpy(geo_map[i])
#             c = c.permute(2, 0, 1)
#             geo_maps.append(c)
#             d = torch.from_numpy(training_mask[i])
#             d = d.permute(2, 0, 1)
#             training_masks.append(d)
#     images = torch.stack(images, 0)
#     score_maps = torch.stack(score_maps, 0)
#     geo_maps = torch.stack(geo_maps, 0)
#     training_masks = torch.stack(training_masks, 0)
#
#     return images, score_maps, geo_maps, training_masks

def collate_fn(batch):
    '''
    Parameter: a batch of data
    Return:
        image_paths: paths of images in this batch
        imgs: (torch.Tensor, bs * 512 * 512 *3) images in this batch
        score_maps: (torch.Tensor, bs * 128 * 128 * 1) score of every pixel in images
        geo_maps: (torch.Tensor, bs * 128 * 128 * 5) 4 distances of this pixel to bound, and 1 orientation of this bboxs
        training_masks: (torch.Tensor, bs * 128 * 128 * 1) mask of gt bbox
        mapping: (ndarray, n) the indices of texts and bboxes
        texts: (ndarray, n) the text of each bbox
        bboxs: (ndarray, n * 9) the coordinates of 4 bbox corners
    '''
    image_paths, img, score_map, geo_map, training_mask, transcripts, boxes = zip(*batch)
    bs = len(score_map)
    images = []
    score_maps = []
    geo_maps = []
    training_masks = []

    for i in range(bs):
        if img[i] is not None:
            a = torch.from_numpy(img[i])
            a = a.permute(2, 0, 1)
            images.append(a)
            b = torch.from_numpy(score_map[i])
            b = b.permute(2, 0, 1)
            score_maps.append(b)
            c = torch.from_numpy(geo_map[i])
            c = c.permute(2, 0, 1)
            geo_maps.append(c)
            d = torch.from_numpy(training_mask[i])
            d = d.permute(2, 0, 1)
            training_masks.append(d)

    images = torch.stack(images, 0)
    score_maps = torch.stack(score_maps, 0)
    geo_maps = torch.stack(geo_maps, 0)
    training_masks = torch.stack(training_masks, 0)

    mapping = []
    texts = []
    bboxs = []
    for index, gt in enumerate(zip(transcripts, boxes)):
        for t, b in zip(gt[0], gt[1]): # 逐一提取transcript, box
            mapping.append(index)
            texts.append(t)
            bboxs.append(b.flatten())

    mapping = np.array(mapping)
    texts = np.array(texts)
    bboxs = np.stack(bboxs, axis=0) # add a new axis
    bboxs = np.concatenate([bboxs, np.ones((len(bboxs), 1))], axis=1).astype(np.float32)
    image_paths = [p.name if isinstance(p, pathlib.Path) else str(p) for p in image_paths]

    return image_paths, images, score_maps, geo_maps, training_masks, texts, bboxs, mapping


# print("Hello World!")
# name = input("What is your name? ")
# print ("I hope you're enjoying yourself " + name)
# age = 2023 - int(input("What is your birth year, " + name + " "))
# print ("You are now " + str(age) + " years old")

# if age > 40:
#    print("You are old, " + response)
# elif age > 30:
#     print("You are in your prime time, " + name)
#else:
#     print("You are young!")

response = "y"
while response == "y":
    weight = float(input("Weight: "))
    units = input("(K)g or (L)bs: ")
    if units.lower()=="l":
        print("Weight in Kg: " + str(weight / 2.205))
    elif units.lower()=="k":
        print("Weight in lbs: " + str(weight * 2.205))
    else:
        print("I'm not sure I can help!")
    response = input("Do you want to try with another weight (y/n)? ")


import numpy as np
import csv
import re
from enum import IntEnum

from ...error_handlers import InvalidUsage
from .Family import FamilyFile, FamilyInfo, get_family_names, get_family_sizes, get_family_emails

# Pew seating file constants
class PewFile(IntEnum):
    SECTION_COL_IDX = 0
    ROW_NUM_IDX = 1
    CAPACITY_IDX = 2

##########################################
####     Error Message functions      ####
##########################################
class ErrorObj():

    def __init__(self, description, filename, row_num, col_num, line_text):
        self.description = description
        self.file = filename
        self.row = row_num
        self.col = col_num
        self.text = line_text
        self._dict = dict()

    def to_dict(self):
        loc = vars(self)
        errors = dict([(i, loc[i]) for i in ('description', 'file', 'row', 'col', 'text')])
        self._dict['errors'] = [ errors ]
        return self._dict

##########################################
####     Input parsing functions      ####
##########################################
def parse_family_file( family_file, filename=None ):
    """
    Reads the CSV file containing family info. The file must be a CSV file,
    and have the following three columns: Family Name, Size of Family, E-mail

    Returns 3 (ordered) lists. Each index contains a piece of a households
    info, so it's important to keep them in this order when assigning to
    populate the seating arrangement with the correct information.
    """
    families = []

    # Discard the first row of column labels
    family_file.readline()

    # Parse the seating chart file
    for i, line in enumerate( family_file.readlines() ):
        # +1 for 1-indexed, and +1 since we skipped the header
        row_num = i + 2

        try:
            line = line.strip()
            row = line.split( "," )

            if len(row) < len(FamilyFile):
                err_obj = ErrorObj( "Family file: Expected 4 columns, but only found " + str( len(row) ) + " columns in this row. "\
                                    "Please fix it and try submitting again.",
                                    filename or "Household Reservations File",
                                    row_num,
                                    -1,
                                    line )
                raise InvalidUsage( err_obj.to_dict() )

            families.append( FamilyInfo( row[FamilyFile.FAMILY_FNAME_IDX],
                                        row[FamilyFile.FAMILY_LNAME_IDX],
                                        int( row[FamilyFile.FAMILY_SIZE_IDX] ),
                                        row[FamilyFile.FAMILY_EMAIL_IDX] ) )

        except ValueError:
            err_obj = ErrorObj( "This cell contains a non-numerical family size value. "\
                                "Please fix it and try submitting again.",
                                filename or "Household Reservations File",
                                row_num,
                                FamilyFile.FAMILY_SIZE_IDX + 1,
                                line )
            raise InvalidUsage( err_obj.to_dict() )

        # Needs to be able to handle subdomains
        EMAIL_REGEX = '^([\w\.\-]+)@([\w\-]+)((\.(\w){2,63}){1,3})$'
        if not re.search( EMAIL_REGEX, row[FamilyFile.FAMILY_EMAIL_IDX] ):
            err_obj = ErrorObj( "This cell contains an invalid e-mail address. "\
                                "Please fix it and try submitting again.",
                                filename or "Household Reservations File",
                                row_num,
                                FamilyFile.FAMILY_EMAIL_IDX + 1,
                                line )
            raise InvalidUsage( err_obj.to_dict() )

    return families

def parse_seating_file( seating_file, filename=None ):
    """
    Reads the CSV file containing pew information. The file must be a CSV file,
    and have the following three columns: Section, Row #, Capacity.

    Returns a tuple of parallel np.arrays of the pew's id and the pew's capacity, respectively.

        (pew_ids, capacities) = parse_seating_file(...)
    """
    pews = []

    # Discard the first row of column labels
    seating_file.readline()

    # Parse the seating chart file
    for i, line in enumerate( seating_file.readlines() ):
        line = line.strip()
        # +1 for 1-indexed, and +1 since we skipped the header
        row_num = i + 2

        try:
            row = line.split( "," )

            if len(row) != len(PewFile):
                err_obj = ErrorObj( "PewFile - Expected 3 columns, but found " + str( len(row) ) + " columns in this row. "\
                                    "Please fix it and try submitting again.",
                                    filename or "Pew Seating Info File",
                                    row_num,
                                    -1,
                                    line )
                raise InvalidUsage( err_obj.to_dict() )

            pews.append( [row[PewFile.SECTION_COL_IDX], row[PewFile.ROW_NUM_IDX], int( row[PewFile.CAPACITY_IDX] )] )

        except ValueError:
            err_obj = ErrorObj( "This cell is empty or contains a non-numerical pew capacity (size) value. "\
                                "Please fix it and try submitting again.",
                                filename or "Pew Seating Info File",
                                row_num,
                                PewFile.CAPACITY_IDX + 1,
                                line )
            raise InvalidUsage( err_obj.to_dict() )

    pews = np.array( pews )
    pews_sorted = pews[np.argsort( pews[:,PewFile.ROW_NUM_IDX] )]

    return __get_pew_ids(pews_sorted), __get_pew_sizes(pews_sorted)

def __get_pew_ids( pew_info ):
    """
    Returns the section and row IDs
    """
    return pew_info[:, PewFile.SECTION_COL_IDX], pew_info[:, PewFile.ROW_NUM_IDX]

def __get_pew_sizes( pew_info ):
    """
    Returns a list of the pew sizes from the pew info matrix.
    """
    return pew_info[:, PewFile.CAPACITY_IDX].astype(int)

##########################################
####         Output re-format         ####
##########################################
def transform_output( optimal_pew_groups, families_left, seatable_family_info ):
    """
    Transforms the optimal seating list into a printable list with family name
    assigned seating.

    Parameters:
        optimal_pew_groups will be formatted as a list of tuples, as follows:
        [ (pew_id, [family_size_1, family_size_2, ...]),
          ...
        ]

        Each tuple will have the pew ID and what family sizes can be seated there.
        The family size values to NOT include the +3 padding. Make sure to call this
        function after re-calculating the actual family sizes.

        seatable_family_info - list of families

    Returns:
        List of tuples with pews and families that can sit there
    """
    family_names = get_family_names( seatable_family_info )
    family_sizes = get_family_sizes( seatable_family_info )

    # Convert family and family sizes to an internal mapping.
    # Family names with size s will exist in a list at map[s - 1].
    family_name_size_map = [[] for _ in range( max( family_sizes ) )]
    for i, size in enumerate( family_sizes ):
        family_name_size_map[size - 1].append( family_names[i] )

    # Convert input into pew_id, list of family names tuple
    result = []
    for pew in optimal_pew_groups:
        # Convert the pew's group sizes to family names
        pew_family_names = []
        for size in pew[1]:
            pew_family_names.append( (family_name_size_map[size - 1].pop(0), size) )
        pew_assign = (pew[0], pew_family_names)

        result.append( pew_assign )

    # Append families that could not fit in pews
    for size in families_left:
        if families_left[size] > 0:
            result.append( (-1, [(family_name_size_map[size - 1].pop(0), size)] ) )

    return result

def get_section_row_str( arr_idx, pew_ids ):
    """
    Converts all the array index row IDs to real door, section and row #s.

    Parameters:
        assigned_seating - a list of tuples containing pew and family assignment
        pew_ids - tuple containing ordered lists of pew sections and row numbers
    """
    sections = pew_ids[0]
    rows = pew_ids[1]
    return sections[arr_idx], sections[arr_idx], rows[arr_idx]

def format_seat_assignments( assigned_seating, family_info, pew_ids, pew_sizes, margin ):
    """
    Input looks like:
    [(row_idx, [('family1_name', family1_size), ...]), ...]
    """
    family_names = get_family_names( family_info )
    family_emails = get_family_emails( family_info )
    family_sizes = get_family_sizes( family_info )

    rows = []
    for pew in assigned_seating:
        next_open_seat = 0

        row_idx = pew[0]
        seating = pew[1]

        for family in seating:
            name = family[0].split(",")
            fname, lname = name[0], name[1]
            size = family[1]

            # Get the family's info to print
            # TODO hacky fix for now, will find a better/faster solution
            idx = family_names.index( family[0] )
            if family_sizes[idx] != size:
                for i in range(idx, len(family_names)):
                    if family_sizes[i] == size and family_names[i] == family[0]:
                       idx = i
                       break

            # If it's an unseated family, there's only one of them in the row
            if row_idx == -1:
                rows.append( ("N", fname, lname, size, family_emails[idx] ) )
                continue

            row = ("N", fname, lname, family_sizes[idx], family_emails[idx])
            row += get_section_row_str( row_idx, pew_ids )

            curr_pew_size = pew_sizes[row_idx]
            seat_nos = []
            if next_open_seat < curr_pew_size:
                # Populate seat #s
                for i in range(size):
                    seat_nos.append( next_open_seat + 1 )
                    next_open_seat += 1

            row += (seat_nos,)
            rows.append( row )

            # If not the end of the pew, add padding
            if next_open_seat != curr_pew_size - 1:
                next_open_seat += margin

    return rows

def write_seat_assignments_csv( seat_assignments_file, formatted_rows ):
    """
    Writes out seat assignments to file.
      - seat_assignments_file: The file object to write the seat assignments to.
      - formatted_rows: A multi-dimensional list of rows to be written in a CSV format.
    """
    csv_out = csv.writer( seat_assignments_file )
    csv_out.writerow( ("Check-in", "First Name", "Last Name", "Size", "E-mail", "Door", "Section", "Row", "Seat #s") )
    csv_out.writerows( formatted_rows )


import subprocess
import time
from binascii import a2b_uu
from enum import Enum

class DeviceLockState(Enum):
    LOCKED_SCREEN_OFF = 0
    LOCKED_SCREEN_ON = 1
    UNLOCKED_SCREEN_OFF = 2
    UNLOCKED_SCREEN_ON = 3

class Device:
    def __init__(self, serial):
        self.serial = serial

    def adb(self, cmd):
        return subprocess.check_output(["adb", "-s", self.serial] + cmd)

    def getprop(self, prop):
        return self.adb(["shell", "getprop", prop]).strip()

    def touch(self, x, y):
        self.adb(["shell", "input", "tap", str(x), str(y)])

    def swipe(self, x1, y1, x2, y2, duration=300):
        self.adb(["shell", "input", "swipe", str(x1), str(y1), str(x2), str(y2), str(duration)])

    def device_lock_state(self):
        power_state = self.adb(["shell", "dumpsys", "power"]).split()
        lockscreen_state = self.adb(["shell", "dumpsys", "window"]).split()
        mWakefulness = [line for line in power_state if b"mWakefulness=" in line][0]
        mShowingLockscreen = [line for line in lockscreen_state if b"mShowingLockscreen=" in line][0]

        if b"mWakefulness=Asleep" in mWakefulness:
            if b"mShowingLockscreen=true" in mShowingLockscreen:
                return DeviceLockState.LOCKED_SCREEN_OFF
            else:
                return DeviceLockState.UNLOCKED_SCREEN_OFF
        elif b"mWakefulness=Awake" in mWakefulness:
            if b"mShowingLockscreen=true" in mShowingLockscreen:
                return DeviceLockState.LOCKED_SCREEN_ON
            else:
                return DeviceLockState.UNLOCKED_SCREEN_ON

    def wake(self):
        self.adb(["shell", "input", "keyevent", "26"])

    def is_activity_running(self, package, activity):
        activities = self.adb(["shell", "dumpsys", "activity", "activities"]).split()
        return bytes(f"realActivity={package}/{activity}", "ascii") in activities

    def start_activity(self, package, activity):
        self.adb(["shell", "am", "start", "-n", package + "/" + activity])

def logprint(*args, **kwargs):
    print("[+]", *args, **kwargs)

sony = Device("ZH80014NDW")

logprint("Device lock state:", sony.device_lock_state())
logprint("Device           :", sony.getprop("ro.product.model"))
logprint("Android          :", sony.getprop("ro.build.version.release"))
logprint("Kernel           :", sony.getprop("ro.build.version.incremental"))

# Set the screen timeout to 30 minutes
logprint("Setting screen timeout to 30 minutes")
sony.adb(["shell", "settings", "put", "system", "screen_off_timeout", "1800000"])

if sony.device_lock_state() == DeviceLockState.LOCKED_SCREEN_OFF or sony.device_lock_state() == DeviceLockState.UNLOCKED_SCREEN_OFF:
    logprint("Screen is off, turning on")
    sony.wake()
    time.sleep(0.5)

if sony.is_activity_running("com.mojang.minecraftpe", ".MainActivity"):
    logprint("Minecraft is running, closing")
    sony.adb(["shell", "am", "force-stop", "com.mojang.minecraftpe"])
    time.sleep(1.0)

sony.start_activity("com.mojang.minecraftpe", ".MainActivity")

logprint("Waiting for Minecraft to start")
while not sony.is_activity_running("com.mojang.minecraftpe", ".MainActivity"):
    time.sleep(0.5)

logprint("Waiting for Minecraft to load")


"""
                             .__  __  ._.
  _____   _______  __ ____   |__|/  |_| |
 /     \ /  _ \  \/ // __ \  |  \   __\ |
|  Y Y  (  <_> )   /\  ___/  |  ||  |  \|
|__|_|  /\____/ \_/  \___  > |__||__|  __
      \/                 \/            \/
a simple code for moving the Kantar Pulls
by Danielle Leong, Data Superstar
no autographs, please~

Created on Wed Nov  9 15:47:05 2016
"""
# referencing the following site:
# https://www.tutorialspoint.com/python/python_files_io.htm
# https://docs.python.org/3/library/os.html

# generate file names
# for all to rename
names = []
for i in range(12):
    if i < 4:
        t = "C3"
        names.append(t + " DEMO " + str(i + 1))
    elif i < 8:
        t = "C7"
        names.append(t + " DEMO " + str(i - 3))
    else:
        t = "LIVE"
        names.append(t + " DEMO " + str(i - 7))

fileNames = ["C3", "LIVE", "PANEL C3", "PANEL LIVE"]
fileNames.extend(names) # used for renaming

counter = 0
for name in fileNames:
    fileNames[counter] = name + ".csv"
    counter = counter + 1

import os
os.chdir("C:/Users/danielle.leong/Desktop/kan")

# splitting and renaming
# http://stackoverflow.com/questions/5749195/how-can-i-split-and-parse-a-string-in-python

# obtain file names
ori = os.listdir("C:/Users/danielle.leong/Desktop/kan")

counter = 0
for f in ori:
    os.rename(f, fileNames[counter])
    counter = counter + 1


score = 70
if score >= 60:
    message = "success"
else:
    message = "failure"

print(message)

#조건부 표현식 : 성공할 때의 조건 먼저 쓰고 뒤에 실패할 때의 실행
message1 = "success" if score >=60 else "failure"
print(message1)


from guardian import GuardState
import kagralib
import vislib
import numpy as np

###########
# Misalign
###########
#MISALIGN_OFFSET = -7500
#MISALIGN_OFFSET = 14000
NOMINAL_BF_OFS = 0
TRAMP_BF_OFS = 10

MISALIGN_OFFSET = -2000
MISALIGN_OFFSET_FOR_PRFPMI = -2000
MISALIGN_TRAMP = 10
MISALIGN_TRAMP_FOR_PRFPMI = 1
MISALIGN_CHAN = 'VIS-PRM_BF_TEST_Y'

###########
# Threshold
###########
THRED_DISTANCE_SET2CURRENT = 300 # If the setpoint and current angle is far from each other by this threshold, guardian will not engage DC loop.

###########
# Offload gain
###########
offload_gain = {
    'PIT':-0.1,
    'YAW':-0.1,
    }

###########
# FB Config.
###########
'''
The following dicts describe the filter configuration. Each dict needs to have the following keys:

keys
----
'chan
'ramptime': float
    ramptime to be set into each filter

'integraotr': bool
    If the filter has integrator, put True.

'init_FM': dict
    FM list which engaged before the filter will be engaged. keys need to be a DoF which the filter has, e.g. 'PIT','YAW','LEN' for K1:VIS-BS_TM_DAMP, 'SF','BF' for K1:VIS-PRM_$(DOF)_DAMP_GAS. Each key value needs to be a list of FM to be engaged, e.g. ['FM1','FM2'].
    The detail of procedure to engage FB is written later.

'bst_FM': dict
    This keyvalue describe the procedure how to engage the FMs after FB is engaged.
    e.g.: [['FM1','FM2'],['FM3']] -> press FM1, FM2 first and then engage FM3 after FM1 and FM2 is engaged.

note
----
- How FMs are engaged
    If integrator is True:
        (0. If gain is 0, change gain to 1 with input off. Wait for ramping)
        1. Turn init_FM on
        2. Engage input.
        3. Toggle first component of bst_FM. Wait for ramping
            4. Toggle second component of bst_FM
                ...

    If integrator is False:
        1. Turn input, output, and init FM on. Clear history.
        2. Change gain to 1. Wait for ramping
        3. Toggle first component of bst_FM. Wait for ramping
            4. Toggle second component of bst_FM
                ...
'''

# K1:VIS-PRM_$(DOF)_DAMP_GAS
GAS_LOCALDAMP = {
    'ramptime':1,
    'integrator':True,
    'init_FM':{
        'SF':['FM2','FM4','FM5','FM6'],
        'BF':['FM2','FM9'],
        },
    'bst_FM':{
        'SF':[[],],
        'BF':[[],]
        }
    }

# K1:VIS-PRM_BF_DAMP
BF_LOCALDAMP = {
    'ramptime':3,
    'integrator':False,
    'init_FM':{
        'LEN':['FM1','FM2','FM4'],
        'TRANS':['FM1','FM2','FM4'],
        'VERT':['FM1','FM2','FM4'],
        'ROLL':['FM1','FM2','FM4','FM5'],
        'PIT':['FM1','FM2','FM4','FM5'],
        'YAW':['FM1','FM2','FM4','FM5'],
    },
    'bst_FM':{
        'LEN':[],
        'TRANS':[],
        'VERT':[],
        'ROLL':[],
        'PIT':[],
        'YAW':[],
    },
    }

# K1:VIS-PRM_IM_DAMP
IM_LOCALDAMP = {
    'ramptime':3,
    'integrator':False,
    'init_FM':{
        'LEN':['FM1','FM2','FM3'],
        'TRANS':['FM1','FM2','FM3'],
        'VERT':['FM1','FM2','FM3','FM4','FM5','FM6'],
        'ROLL':['FM1','FM2','FM3','FM4'],
        'PIT':['FM1','FM2','FM3'],
        'YAW':['FM1','FM2','FM3','FM4'],
    },
    'bst_FM':{
        'LEN':[],
        'TRANS':[],
        'VERT':[],
        'ROLL':[],
        'PIT':[],
        'YAW':[],
    },
    }

#K1:VIS-PRM_TM_DAMP
TM_OLDAMP = {
    'gain':1,
    'ramptime':0,
    'integrator':False,
    'init_FM':{'LEN':[],'PIT':[],'YAW':[]},
    'bst_FM':{'LEN':[],'PIT':[],'YAW':[]},
    }

#K1:VIS-PRM_IM_OLDAMP
IM_OLDAMP = {
    'gain':1,
    'ramptime':1,
    'integrator':False,
    'init_FM':{'PIT':['FM1','FM2','FM3'],'YAW':['FM1','FM2','FM3','FM4','FM8']},
    'bst_FM':{'PIT':[],'YAW':[]},
    'DC_FM':{'LEN':[],'PIT':[['FM1',],['FM10',]],'YAW':[['FM1',],['FM9',]]},
}

#K1:VIS-PRM_TM_OPLEV_SERVO
OLSERVO = {
    'gain':1,
    'ramptime':3,
    'integrator':False,
    'init_FM':{'LEN':['FM1','FM2','FM9'],'PIT':['FM1','FM2','FM9'],'YAW':['FM1','FM2','FM9']},
    'bst_FM':{'LEN':[],'PIT':[],'YAW':[]},
    'DC_FM':{'LEN':[],'PIT':[['FM1',],['FM3','FM4','FM8']],'YAW':[['FM1',],[]]},
}

from TypeBp import INIT, SAFE, TRIPPED, nominal
from GRD_OLD import ENGAGE_GAS_LOCALDAMP, ENGAGE_BF_LOCALDAMP, TWR_DAMPED, DISABLE_BF_LOCALDAMP, DISABLE_GAS_LOCALDAMP, ENGAGE_IM_LOCALDAMP, PAY_LOCALDAMPED,  ENGAGE_TM_OLDAMP, ENGAGE_IM_OLDAMP, ENGAGE_OLSERVO, ALIGNED, DISABLE_OLSERVO, DISABLE_IM_OLDAMP, DISABLE_TM_OLDAMP, PAY_LOCALDAMPED, TRANSIT_TO_OBS, OBSERVATION, BACK_TO_ALIGNED, TWR_IDLE, DISABLE_IM_LOCALDAMP, edges, MISALIGNING, MISALIGNED, REALIGNING, check_WD, check_TWWD, OLDAMPED, ENGAGE_IM_OLDC, DISABLE_IM_OLDC, ENGAGE_OLSERVO_DC, DISABLE_OLSERVO_DC,TRANSIT_TO_LOCKACQ, LOCK_ACQUISITION, BACK_TO_LOCKACQ, ENGAGE_BF_OLDAMP, DISABLE_BF_OLDAMP

#########################
# class
#########################
class MISALIGNING_FOR_PRFPMI(GuardState):
    index = 99
    request = False

    @check_TWWD
    @check_WD
    def main(self):
        self.counter = 0
        self.timer['waiting'] = 0

    @check_TWWD
    @check_WD
    def run(self):
        if not self.timer['waiting']:
            return

        elif self.counter == 0:
            ezca.get_LIGOFilter('VIS-PRM_TM_OPLEV_SERVO_YAW').ramp_offset(np.sign(ezca['VIS-PRM_TM_OLSET_Y_OUTPUT'])*400,5,False)
            self.counter += 1
            self.timer['waiting'] = 5

        elif self.counter == 1:
            return True

class MISALIGNED_FOR_PRFPMI(GuardState):
    index = 97
    request = True

    @check_TWWD
    @check_WD
    def run(self):
        return True

#Overwrite misalign state in VIS_GRD.py
class MISALIGNING(GuardState):
    index = 101
    request = False

    def is_PSD_centered(self):
        pit = ezca['VIS-PRM_TM_PSD_PIT_OUTPUT']
        yaw = ezca['VIS-PRM_TM_PSD_YAW_OUTPUT']
        if abs(pit)<0.1 and abs(yaw)<0.1:
            return True
        else:
            return False

    @check_TWWD
    @check_WD
    def main(self):
        self.timer['waiting'] = 0
        self.timer['warning'] = 0
        self.counter = 0
        self.OPAL = {DoF:vislib.OpticAlign('PRM',DoF) for DoF in ['PIT','YAW']}
        self.IM_TEST = {DoF:ezca.get_LIGOFilter('VIS-PRM_IM_TEST_%s'%DoF[0]) for DoF in ['PIT','YAW']}
        self.BF_TEST = ezca.get_LIGOFilter('VIS-PRM_BF_TEST_Y')
        self.timer['checking'] = 0

    @check_TWWD
    @check_WD
    def run(self):
        if not self.timer['waiting']:
            return

        if bool(ezca['PSL-BEAM_SHUTTER_STATE']) and self.counter < 1:
            if self.timer['warning']:
                kagralib.speak_aloud('PSL shutter is open. Please close it to misalign PRM')
                notify('PSL shutter is open. Please close it to misalign PRM')
                self.timer['warning'] = 30
            return

        if self.counter == 0:
            for key in self.OPAL:
                self.OPAL[key].turn_off('OFFSET')
                self.IM_TEST[key].TRAMP.put(3)
                self.IM_TEST[key].turn_on('OFFSET')
                self.IM_TEST[key].ramp_gain(1,0,False)
            self.counter += 1
            self.timer['waiting'] = 1

        elif self.counter == 1:
            self.BF_TEST.TRAMP.put(3)
            self.BF_TEST.turn_on('OFFSET')
            self.BF_TEST.ramp_gain(1,0,False)
            self.timer['warning'] = 120
            self.counter += 1

        elif self.counter == 2 and not self.BF_TEST.is_offset_ramping():
            # engage PSD loop in IM_PIT and BF_YAW
            filt = ezca.get_LIGOFilter('VIS-PRM_BF_PSD_Y')
            filt.switch('FM3','FM7','FM8','FM9','ON')
            filt.ramp_gain(1,ramp_time=5,wait=True)
            filt = ezca.get_LIGOFilter('VIS-PRM_IM_PSD_P')
            filt.switch('FM3','FM7','FM8','FM9','ON')
            filt.ramp_gain(1,ramp_time=5,wait=True)

            if self.is_PSD_centered() and self.timer['checking']:
                self.timer['checking'] = 10
                self.counter += 1

        elif self.counter==3:
            if self.timer['checking']:
                if self.is_PSD_centered():
                    return True
            if self.timer['warning']:
                kagralib.speak_aloud('PRM is not misaligned propery yet. Please check PRM status.')
                self.timer['warning'] = 120

#[FIXME]
'''
class ENGAGE_PSD_LOOP(engage_damping):
    index = 106
    request = False
    #[FIXME]
    pass
'''

class REALIGNING(GuardState):
    index = 103
    request = False

    def is_oplev_inrange(self):
        #[FIXME] check oplev position
        #inrange = all([ (abs(ezca[vislib.DiagChan('PRM',DoF)]) < 400) for DoF in ['PIT','YAW']])
        #oksum = ezca['VIS-PRM_TM_OPLEV_TILT_SUM_OUTPUT'] > 3000
        #return (inrange and oksum)
        return all([ (abs(ezca[vislib.DiagChan('PRM',DoF)]) < 400) for DoF in ['PIT','YAW']]) and ezca['VIS-PRM_TM_OPLEV_TILT_SUM_OUTPUT'] > 3000

    @check_TWWD
    @check_WD
    def main(self):
        self.timer['waiting'] = 0
        self.timer['warning'] = 0
        self.counter = 0
        self.OPAL = {DoF:vislib.OpticAlign('PRM',DoF) for DoF in ['PIT','YAW']}
        self.IM_TEST = {DoF:ezca.get_LIGOFilter('VIS-PRM_IM_TEST_%s'%DoF[0]) for DoF in ['PIT','YAW']}
        self.IM_OFFSET = {'PIT':-1500,'YAW':-3000}
        self.BF_TEST = ezca.get_LIGOFilter('VIS-PRM_BF_TEST_Y')
        self.BF_OFFSET = NOMINAL_BF_OFS

    @check_TWWD
    @check_WD
    def run(self):
        if not self.timer['waiting']:
            return

        if bool(ezca['PSL-BEAM_SHUTTER_STATE']) and self.counter < 1:
            if self.timer['warning']:
                kagralib.speak_aloud('PSL shutter is open. Please close it to realign PRM')
                notify('PSL shutter is open. Please close it to realign PRM')

                self.timer['warning'] = 30
            return

        if self.counter == 0:
            for key in self.OPAL:
                self.OPAL[key].turn_on('OFFSET')
                self.IM_TEST[key].turn_off('OFFSET')
            self.IM_TEST['PIT'].ramp_offset(self.IM_TEST['PIT'].OFFSET.get()+ezca['VIS-PRM_IM_PSD_P_OUTPUT'],1,False)
            self.counter += 1
            self.timer['waiting'] = 1

        elif self.counter == 1:
            self.BF_TEST.turn_off('OFFSET')
            self.BF_TEST.ramp_offset(self.BF_TEST.OFFSET.get() + ezca['VIS-PRM_BF_PSD_Y_OUTPUT'],1,False)
            self.timer['warning'] = 120
            self.counter += 1

        elif self.counter == 2 and not self.BF_TEST.is_offset_ramping():
            # diable PSD loop in IM_PIT and BF_YAW
            filt = ezca.get_LIGOFilter('VIS-PRM_BF_PSD_Y')
            filt.switch('FM3','FM4','FM7','FM8','FM9','OFF')
            filt.ramp_gain(0,ramp_time=5,wait=True)
            filt = ezca.get_LIGOFilter('VIS-PRM_IM_PSD_P')
            filt.switch('FM3','FM4','FM7','FM8','FM9','OFF')
            filt.ramp_gain(0,ramp_time=5,wait=True)

            if self.timer['warning']:
                kagralib.speak_aloud('PRM is not realigned propery yet. Please check PRM status.')
                self.timer['warning'] = 120

            return self.is_oplev_inrange()
#[FIXME]
'''
class DISABLE_PSD_LOOP(disable_damping):
    index = 107
    request = False

    pass
'''

class REALIGNING_FOR_PRFPMI(GuardState):
    index = 98
    request = False

    @check_TWWD
    @check_WD
    def main(self):
        self.counter = 0
        self.timer['waiting'] = 0

    @check_TWWD
    @check_WD
    def run(self):
        if not self.timer['waiting']:
            return

        elif self.counter == 0:
            ezca.get_LIGOFilter('VIS-PRM_TM_OPLEV_SERVO_YAW').ramp_offset(0,5,False)
            self.counter += 1
            self.timer['waiting'] = 5

        elif self.counter == 1:
            return True

edges += (
    ('ALIGNED','MISALIGNING_FOR_PRFPMI'),
    ('MISALIGNING_FOR_PRFPMI','MISALIGNED_FOR_PRFPMI'),
    ('MISALIGNED_FOR_PRFPMI','REALIGNING_FOR_PRFPMI'),
    ('REALIGNING_FOR_PRFPMI','ALIGNED'),
    )


from flask import Flask
from flask import render_template
from flask import session
from flask import request
from flask import redirect
from flask import flash

from mysqlconnection import MySQLConnector

import re
email_regex = re.compile(r'^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9._-]+\.[a-zA-Z]+$')

app = Flask(__name__)
app.secret_key = 'SSSH'
mysql = MySQLConnector(app, 'emaildb')

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

@app.route('/email', methods= ['post'])
def process():
	if not email_regex.match(request.form['email']):
		flash("Invalid Email Address!")
		return redirect('/')
	query = "INSERT INTO email (email, created_at) VALUES (:email, NOW())"
	data = {
			'email' : request.form['email']
			}
	email = mysql.query_db(query, data)

	query = "SELECT * FROM email"
	email = mysql.query_db(query)
	return render_template('/success.html', all_email=email)

@app.route('/success')
def show_user():
	return render_template('success.html')

app.run(debug=True)


import Utils

def bruteForce(points, ctr):
    if (len(points) <= 1):
        return None, None, ctr

    closestPairs = []
    for i in range(len(points)):
        for j in range(i+1, len(points)):
            if (i == 0 and j ==1):
                shortestDistance, ctr = Utils.getDistancePoints(points[i], points[j], ctr)
                closestPairs += [[points[i], points[j]]]
            else :
                distPoint, ctr = Utils.getDistancePoints(points[i], points[j], ctr)
                if (distPoint < shortestDistance):
                    closestPairs = []
                    shortestDistance = distPoint
                    closestPairs = [[points[i], points[j]]]
                elif (distPoint == shortestDistance):
                    closestPair = [points[i], points[j]]
                    closestPairs = Utils.appendIfNotSame(closestPairs, closestPair)

    return closestPairs, shortestDistance, ctr


import sys
import sqlite3

from PyQt5 import uic
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem

class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi("main.ui", self)
        self.setWindowTitle('Кофе')
        self.con = sqlite3.connect("coffee_db.sqlite")
        self.cur = self.con.cursor()
        self.dobav_tabl()

    def dobav_tabl(self):
        zapr_tb = self.cur.execute("""SELECT * FROM info_coffee""").fetchall()
        print(zapr_tb)
        self.rowPosition = self.table.rowCount()
        for i in range(len(zapr_tb)):
            self.table.insertRow(self.rowPosition)
            for j in range(7):
                if j != 2 and j != 3:
                    self.table.setItem(self.rowPosition, j, QTableWidgetItem(str(zapr_tb[i][j])))
                elif j == 2:
                    f = self.cur.execute(f"""SELECT title FROM degree_of_roast
                    WHERE id = {zapr_tb[i][j]}""").fetchall()
                    self.table.setItem(self.rowPosition, j, QTableWidgetItem(str(f[0][0])))
                elif j == 3:
                    k = 'Молотый' if str(zapr_tb[i][j]) == '1' else 'В зернах'
                    self.table.setItem(self.rowPosition, j, QTableWidgetItem(k))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyWidget()
    ex.show()
    sys.exit(app.exec())


from time import perf_counter, time

import numpy as np
import pinocchio as pino
from copy import copy
from _sst_module import SSTWrapper, IiwaAcc, IiwaAccDistance, IiwaKinoRectangleObs3DSystem, IiwaKinoDistance

from manifold_planning.utils.constants import Table1, Table2

class IiwaKinoRectangleObs3D(IiwaKinoRectangleObs3DSystem):
    def __init__(self, obstacle_list):
        super().__init__(obstacle_list)

    def distance_computer(self):
        # return euclidean_distance(np.array(self.is_circular_topology()))
        return IiwaKinoDistance()
        # return IiwaAccDistance()

def forward_kinematics(pino_model, pino_data, q):
    pino.forwardKinematics(pino_model, pino_data, q)
    pino.updateFramePlacements(pino_model, pino_data)
    xyz_pino = pino_data.oMf[-1].translation
    R_pino = pino_data.oMf[-1].rotation
    return copy(xyz_pino), copy(R_pino)

class SSTPlanner:
    def __init__(self, pino_model, joint_id):
        self.pino_model = pino_model
        self.pino_data = self.pino_model.createData()
        self.joint_id = joint_id
        self.D = 7
        self.config = dict(
            random_seed=0,
            goal_radius=0.2,
            sst_delta_near=0.2,
            sst_delta_drain=0.1,
            integration_step=0.005,
            min_time_steps=1,
            max_time_steps=20,
            number_of_iterations=300000,
            max_planning_time=60.,
        )

    def solve(self, q0, qk):
        start_state = q0[:self.D].tolist() + np.zeros(self.D).tolist()
        goal_state = qk[:self.D].tolist() + np.zeros(self.D).tolist()

        xyz_0, _ = forward_kinematics(self.pino_model, self.pino_data, q0)
        xyz_d, _ = forward_kinematics(self.pino_model, self.pino_data, qk)

        obs = np.array([[Table1.xl, Table1.yl, -1e10, Table1.xh, Table1.yh, xyz_0[-1]],
                        [Table2.xl, Table2.yl, -1e10, Table2.xh, Table2.yh, xyz_d[-1]]])
        system = IiwaKinoRectangleObs3D(obs)

        planner = SSTWrapper(
            state_bounds=system.get_state_bounds(),
            control_bounds=system.get_control_bounds(),
            distance=system.distance_computer(),
            start_state=start_state,
            goal_state=goal_state,
            goal_radius=self.config['goal_radius'],
            random_seed=0,
            sst_delta_near=self.config['sst_delta_near'],
            sst_delta_drain=self.config['sst_delta_drain']
        )

        tic = perf_counter()
        for iteration in range(self.config["number_of_iterations"]):
            planner.step(system, self.config["min_time_steps"], self.config["max_time_steps"],
                         self.config["integration_step"])
            if perf_counter() - tic > self.config["max_planning_time"] or planner.get_solution() is not None:
                break

        planning_time = perf_counter() - tic

        solution = planner.get_solution()
        solution = solution if solution is not None else planner.get_approximate_solution()
        solution = solution if solution is not None else []

        q = np.array([q0])
        dq = np.zeros_like(q)
        t = np.zeros((1,))
        if len(solution):
            q = solution[0][:, :self.D]
            dq = solution[0][:, self.D:]
            t = np.concatenate([[0.], solution[2]])
            t = np.cumsum(t)
        return q, dq, [], t, planning_time