import argparse
import json
import os
import sys

sys.path.append(".")

import numpy as np
from allennlp.models.archival import load_archive
from allennlp.predictors.predictor import Predictor
from sequential_sentence_classification.sequential_sentence_classification import (
    dataset_reader,
    model,
    predictor,
)
from tqdm import tqdm

from utils import SSC_OUTPUT_DIR, SSC_ABSTRACT_OUTPUT_DIR

def run_ssc(model_path: str, test_jsonl_file: str, output_json_file: str):
    text_field_embedder = {
        "token_embedders": {
            "bert": {
                "pretrained_model": "https://ai2-s2-research.s3-us-west-2.amazonaws.com/scibert/allennlp_files/scibert_scivocab_uncased.tar.gz"
            }
        }
    }
    token_indexers = {
        "bert": {
            "pretrained_model": "https://ai2-s2-research.s3-us-west-2.amazonaws.com/scibert/allennlp_files/scivocab_uncased.vocab"
        }
    }
    overrides = {
        "model": {"text_field_embedder": text_field_embedder},
        "dataset_reader": {
            "token_indexers": token_indexers,
            "use_sep": False,
            "predict": True,
        },
    }

    model_archive = load_archive(
        model_path, overrides=json.dumps(overrides), cuda_device=2
    )
    predictor = Predictor.from_archive(model_archive, "SeqClassificationPredictor")
    dataset_reader = predictor._dataset_reader
    model = predictor._model
    idx_to_label_map = model.vocab._index_to_token["labels"]

    test_jsons = []
    with open(test_jsonl_file) as f:
        for line in f:
            test_jsons.append(json.loads(line))
    print("{} test jsons loaded".format(len(test_jsons)))

    result = {}
    with open(output_json_file, "w") as out_f:
        for json_dict in tqdm(test_jsons, desc="Predicting..."):
            sentences = json_dict["sentences"]
            if not sentences:
                continue

            instances = dataset_reader.read_one_example(json_dict)
            if not isinstance(instances, list):
                instances = [instances]

            # with open(f"scripts/sentences/{json_dict['abstract_id']}.json", "r") as f:
            #     id_to_sent = json.load(f)
            # sent_to_id = {s: id for id, s in id_to_sent.items()}

            scores_list = []
            for instance in instances:
                prediction = predictor.predict_instance(instance)
                probs = prediction["action_probs"]
                scores_list.extend(probs)

            if len(sentences) != len(scores_list):
                print(
                    "The following paper had a mismatched number of sentences and sentence predictions."
                )
                print(json_dict.get("abstract_id", "") or json_dict.get("paper_id", ""))
                continue
            sentences_with_scores = list(zip(sentences, scores_list))
            sentences_with_scores = sorted(
                sentences_with_scores, key=lambda x: x[1], reverse=True
            )

            preds = []
            for sentence, scores in sentences_with_scores:
                pred_idx = np.argmax(scores)
                pred_label = idx_to_label_map[pred_idx].split("_")[0].capitalize()
                pred_prob = scores[pred_idx]
                preds.append(
                    {"sentence": sentence, "label": pred_label, "prob": pred_prob,}
                )
            if "abstract_id" in json_dict:
                result[json_dict["abstract_id"]] = preds
            elif "paper_id" in json_dict:
                result[json_dict["paper_id"]] = preds
            else:
                continue
        json.dump(result, out_f, indent=2)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--path_to_model", help="Path to the model to use for prediction", required=True
    )
    parser.add_argument(
        "--test_jsonl_file",
        help="Path to the jsonl file containing sentences to predict",
        required=True,
    )
    parser.add_argument(
        "--output_file", "-o", help="Name of file to output json predictions.",
    )
    args = parser.parse_args()

    if args.output_file:
        output_filename = args.output_file
    else:
        output_filename = (
            os.path.splitext(os.path.basename(args.test_jsonl_file))[0] + ".json"
        )
    output_json_file = os.path.join(SSC_OUTPUT_DIR, output_filename)
    run_ssc(args.path_to_model, args.test_jsonl_file, output_json_file)


## @package enhanced_grid

"""
Written by Daniel M. Aukes and CONTRIBUTORS
Email: danaukes<at>asu.edu.
Please see LICENSE for full license.
"""

## @brief Contains 2D and 3D Grid containers that supports extended slicing syntax.
##
# These classes are provided for rapid prototyping, the methods defined on them might be slow.
# @code
# grid1 = Grid3D(10, 10, 10, 0)
# grid2 = Grid3D(10, 10, 10, 1)
# grid1[0, 0, 0] = grid2[0, 0, 0]
# grid1[0, 0, 2:6:2] = grid2[0, 0, 4:5]
# grid1[0, 0, ...] = grid2[0, 0, ...]
# grid1[0, ..., 0] = grid2[..., 0, 0]
# grid1[..., ..., ...] = grid2[..., ...., ....]
# @endcode
#
# Slicing does not copy elements - an auxiliary window container is created that delegates
# further operations to the underlying container.
# Note that assignments to slices from the same object might not behave as espected.
# Parallel assignment also does not always work as expected.
# For example:
#@code
# grid[..., 0], grid[..., 1] = grid[..., 1], grid[..., 0]
#@endcode
# does not correctly swop two rows, but the following does:
#@code
# grid[..., 0], grid[..., 1] = grid[..., 1].clone(), grid[..., 0].clone()
#@endcode
#Strictly speaking, it is necessary only to clone the one object,
#but it is hard to remember which, so it is better to clone both (?).
#
#@todo Implement a way to use copy containers instead of window containers

from __future__ import division
from random import randint
from math import floor
from math import ceil
from array import array

##@brief A class that works just like a queue or a stack, except
## that a randomly selected element is returned.
##
# This class is useful for implementing algorithms that gather
# elements, and need to process them randomly. Something along the
# lines of:
#
# @code
# while not rqueue.empty():
#   #generates 3 new elements to process
#   for i in range(3):
#     rqueue.push(process(rqueue.pop()))
# @endcode
class RandomQueue:
    ## Constructs a new empty RandomQueue
    def __init__(self):
        ## The internal list to store objects.
        self.array = []

    ##Returns True if this RandomQueue is empty.
    def empty(self):
        return len(self.array) <= 0

    ## Push a new element into the RandomQueue.
    def push(self, x):
        self.array.append(x)

    ## @brief Pops a randomly selected element from the queue.
    ##
    # All elements can be selected equiprobably
    def pop(self):
        n = len(self.array)

        if n <= 0:
            raise IndexError('Cannot pop from emty container!')
        elif n == 1:
            return self.array.pop()
        else:
            i = randint(0, n - 1)
            j = n - 1
            self.array[i], self.array[j] =     self.array[j], self.array[i]

        return self.array.pop()

## @brief Class that represents a 2D array.
##
# The following convenient syntax is supported:
# @code
# p = 2, 3 # a coordinate in the grid
# grid[p] = 5
# print grid[p]
# print grid[2, 3]
# @endcode
#

def signum(x):
    if x > 0:
        return 1
    elif x < 0:
        return -1
    else:
        return 0

## Truncates a point to integer coordinates.
def int_point_2d(p):
    x, y = p
    return int(x), int(y)

## Truncates a point to integer coordinates.
def int_point_3d(p):
    x, y, z = p
    return int(x), int(y), int(z)

# Every point in the sample set is represented with
# a 1 in th grid; all other points are represented with 0.
# The returned grid is usefull for producing image data.
def points_to_grid(points, dimensions):
    grid = Grid2D(dimensions, 0)

    for point in points:
        grid[int_point_2d(point)] = 1

    return grid

## Converts a list of points to a 3D grid.

# Every point in the sample set is represented with
# a 1 in th grid; all other points are represented with 0.
# The returned grid is usefull for producing image data.
def points_to_grid_3d(points, dimensions):
    grid = Grid3D(dimensions, 0)

    for point in points:
        grid[int_point_3d(point)] = 1

    return grid

def make_grid_1d(width, initial_item=None):
    grid = [initial_item] * width

    return grid

## @brief Makes 2 list of lists.
def make_grid_2d(width, height, initial_item=None):
    grid = [None] * width

    for i in xrange(width):
        grid[i] = [None] * height

        for j in xrange(height):
            grid[i][j] = initial_item

    return grid

## @brief Makes 2 list of lists.
def make_grid_3d(width, height, depth, initial_item):
    grid = [None] * width

    for i in xrange(width):
        grid[i] = [None] * height

        for j in xrange(height):
            grid[i][j] = [None] * depth

            for k in xrange(depth):
                grid[i][j][k] = initial_item

    return grid

## @brief Returns an xrange that can be used to iterate over
## the slice of the container.
#
# The following snippets are equivalent
#@code
# s = slice(3, 18, 3)
# for i in srange(s):
#    print list[i]
#@endcode
#
#@code
# for item in list[s]:
#    print item
#@endcode
def srange(s, length):
    if s == Ellipsis:
        return xrange(length)
    else:
        b, e, s = s.indices(length)
        return xrange(b, e, s)

## @brief Returns true if s is a slice or an Ellipsis.
def is_slice(s):
    return type(s) is slice or s == Ellipsis

## @brief Returns the number of elements this slice will return, provided the provided
## primary is large enough.
def slice_len(s, length):
    if s == Ellipsis:
        return length

    b, e, s, = s.indices(length)

    tmp = int(ceil((e - b) / s))

    if tmp < 0:
        return 0
    else:
        return min(tmp, length)

    if s.stop > s.start and s.step > 0:
        return (s.stop - s.start) // s.step
    elif s.stop < s.start and s.step < 0:
        return (s.start - s.stop) // -s.step
    else:
        return 0

## @brief Returns a slice that is equivalent to the two slices combined.
##
# The following snippets are equivalent:
#@code
# list[s1][s2]
#@endcode

#@code
# list[slice_mul(s1, s2)]
#@endcode
def slice_mul(slice1, slice2, length):
    if type(slice2) is int:
        if type(slice1) is type(Ellipsis):
            return slice2
        b1, e1, s1 = slice1.indices(length)
        s2 = slice2
        if s2 < 0:
            s2 += length
        if s2 < 0:
            s2 = 0
        return b1 + s2*s1
    elif type(slice2) is slice:
        if type(slice1) is type(Ellipsis):
            return slice2
        else:
            b1, e1, s1 = slice1.indices(length)
            b2, e2, s2 = slice2.indices(length)
            b = b1 + b2*s1
            s = s1*s2
            e = min(b1 + e2*s1, e1)

            if e < 0 and s < 0:
                return slice(b, None, s)
            else:
                return slice(b, e,  s)

            b = slice1.start + slice2.start*slice1.step
            s = slice1.step*slice2.step
            return slice(b, min(slice1.start + slice2.stop*slice1.step, slice1.stop), s)
    elif slice2 == Ellipsis:
        return slice1

## @brief Completes this slice for a given length.
##
# The resulting slice will give the same elements for a container of the given length, but
# none of the start, stop, or step attributes will be None. If s is the Ellipses, then
# the slice (0, length, 1) is returned.
# @deprecated
def complete_slice(s, length):
    return s

## @brief Sub-classes of this container can be used directly.
##
## A principle container will do assignment on a 1D point.
## Assignment of larger chunks is delegated to the AuxilaryContainer
## with the right dimensions.
##
## The enherritance is merely for documentation purposes.
class PrincipleContainer:
    pass

## @brief Sub-classes of this container is used as windows by a
##PrincipleContainer, and should not be used directly!
##
## An AuxiliaryContainer will do assignment a chunk with the same
## dimensionality as itself, otherwise it delegates it to the underlying
## principle container, which will, in turn, construct the correct
## AuxiliaryContainer to perform the assignment, or perform it if it is
## a 1D point.
##
## The enherritance is merely for documentation purposes.
class AuxiliaryContainer:
    pass

##Abstract super class of all grid-like containers.
##These containers are static, that is, once they are created,
##their dimensions cannot change.
#
# Children of this class must implement
# the attribute __clonetype__, which must
# hold a callable type that can be constructed
# for clones.
#
# Children must also implement the iterators
# cell_iter() and index_iter().
class Container:
    ##
    ##
    # @param dim
    #        The dimensions of this grid
    def __init__(self, dims):
        self.dims = dims

        count = 1

        for dim in dims:
            count *= dim

        self.count = count

    ##Test whether two containers have the same dimensions
    ##and the same items at equal indices.
    def __eq__(self, other):
        if other == None:
            return False
        if self.dims != other.dims:
            return False

        for cell1, cell2 in zip(self.cell_iter(), other.cell_iter()):
            if cell1 != cell2:
                return False
        return True

    ##Equivalent to @code not (self == other) @endcode .
    def __ne__(self, other):
        return not (self == other)

    ## Returns the length (1D) or width (nD) of this container.
    #
    # The length of a container is defined such that the length behaves as it would
    # for a list of lists.
    def __len__(self):
        return self.dims[0]

    ## @brief Returns the minimum and maximum elements
    ## of this grid as a tuple.
    ##
    #This method assumes the grid is filled.
    def min_max(self):
        cell_iter = self.cell_iter()
        min = max = cell_iter.next()

        for cell in cell_iter:
            if cell > max:
                max = cell
            elif cell < min:
                min = cell
        return min, max

    def copy_from(self, other):
        for index in self.index_iter():
            self[index] = other[index]

    ##Makes a shallow copy of this container.
    #
    #This method constructs an instance of
    #this instance's __clonetype__. In general,
    #if this class is an AuxiliaryContainer,
    #the clone will be a PrincipleContainer
    #of the same dimension.
    def clone(self):
        new_grid = self.__clonetype__(self.dims)
        new_grid.copy_from(self)

        return new_grid

## Class that implements __str__ and __iter__.
class Container1D (Container):
    def __init__(self, length):
        Container.__init__(self, (length,))
        self.length = length
        self.__clonetype__ = Grid1D

    def __str__(self):
        #slow...
        glst = []

        for i in xrange(self.length):
            glst.append(self[i])

        return glst.__repr__()

    def __iter__(self):
        for i in xrange(self.length):
            yield self[i]
        raise StopIteration

    ## @brief Returns the same iterator as __iter__.
    #
    # Provided so that all containers have consistent cell_iter methods.
    def cell_iter(self):
        return self.__iter__()

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iteratir will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    # The iterator does not iterate outside the grid.
    def window_iter(self, x1, x0):
        for i in xrange(max(0, x0), min(x1, self.length)):
            yield self[i]
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iteratir will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    # The iterator wraps over the grid. For example, if x is one unit too high
    # (it is outside the grid to the right), the iterator will return first
    # cell in that row.
    def wrapped_window_iter(self, x1, x0):
        for i in xrange(x0, x1):
            yield self[i % self.length]
        raise StopIteration

    ## @brief Returns an iterator that iterates over all cells in the square
    ## surrounding the given point.
    ##
    #The square is 2*n + 1 units.
    def square_iter(self, x, n):
        return self.window_iter(x - n, x + n + 1)

    def wrapped_square_iter(self, x, n):
        return self.wrapped_window_iter(x - n, x + n + 1)

    ## @brief Returns an iterator that iterates over the indeces of this
    ## container.
    ##
    # If grid is a 2 by 2 grid, then:
    # @code
    # for p in index_iter(grid):
    #     print p
    # @endcode
    # will produce
    # @code
    # 0, 0
    # 0, 1
    # 1, 0
    # 1, 1
    # @endcode
    # This iterator is useful for assigning elements of grids:
    # @code
    # for p in index_iter(grid):
    #     grid[p] = random()
    # @endcode
    def index_iter(self):
        for i in xrange(self.length):
            yield i
        raise StopIteration

## Class that implements __str__ and __iter__.
class Container2D (Container):
    def __init__(self, width, height):
        Container.__init__(self, (width, height))
        self.width = width
        self.height = height
        self.__clonetype__ = Grid2D

    def __str__(self):
        #slow...
        glst = []

        for i in xrange(self.width):
            gcol = []

            for j in xrange(self.height):
                gcol.append(self[i, j])

            glst.append(gcol)

        return glst.__repr__()

    ## @brief Returns an iterator that iterates over columns.
    ##
    # This iterator is provided so that a Grid2D better emulates a list of
    # lists, as in the following example:
    #@code
    #     for col in grid:
    #        for item in col:
    #            process(item)
    #@endcode
    # Use of this iterator is discouraged - it is slow
    def __iter__(self):
        for i in xrange(self.width):
            yield self[i, ...]
        raise StopIteration

    ## @brief Returns an iterator that iterates over all cells in the grid.
    ##
    # This allows you to write:
    #@code
    # for cell in cell_iter(grid):
    #   process(cell)
    #@endcode
    def cell_iter(self):
        for i in xrange(self.width):
            for j in xrange(self.height):
                yield self[i, j]
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    #
    def window_index_iter(self, p0, p1):
        x0, y0 = p0
        x1, y1 = p1
        for i in xrange(max(0, x0), min(x1, self.width)):
            for j in xrange(max(0, y0), min(y1, self.height)):
                yield (i, j)
        raise StopIteration
    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    #
    # The iterator wraps over the grid. For example, if x is one unit too high
    # (it is outside the grid to the right), the iterator will return the index of the
    # first cell in that row.
    def wrapped_window_index_iter(self, p0, p1):
        x0, y0 = p0
        x1, y1 = p1
        for i in xrange(x0, x1):
            for j in xrange(y0, y1):
                yield (i % self.width, j % self.height)
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    def window_iter(self, p0, p1):
        x0, y0 = p0
        x1, y1 = p1
        for i in xrange(max(0, x0), min(x1, self.width)):
            for j in xrange(max(0, y0), min(y1, self.height)):
                yield self[i, j]
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    #@endcode
    #
    # The iterator wraps over the grid. For example, if x is one unit too high
    # (it is outside the grid to the right), the iterator will return first
    # cell in that row.
    def wrapped_window_iter(self, p0, p1):
        x0, y0 = p0
        x1, y1 = p1
        for i in xrange(x0, x1):
            for j in xrange(y0, y1):
                yield self[i % self.width, j % self.height]
        raise StopIteration

    ## @brief Returns an iterator that iterates over all cells in the square
    ## surrounding the given point.
    ##
    #The square is 2*n + 1 units.
    def square_index_iter(self, p, n):
        x, y = p
        return self.window_index_iter((x - n, y - n), (x + n + 1, y + n +1))

    ## @brief Returns an iterator that iterates over all cells in the square
    ## surrounding the given point.
    ##
    #The square is 2*n + 1 units.
    # The iterator wraps over the grid. For example, if x is one unit too high
    # (it is outside the grid to the right), the iterator will return first
    # cell in that row.
    def wrapped_square_index_iter(self, p, n):
        x, y = p
        return self.wrapped_window_index_iter((x - n, y - n), (x + n + 1, y + n +1))

    ## @brief Returns an iterator that iterates over all cells in the square
    ## surrounding the given point.
    ##
    #The square is 2*n + 1 units.
    def square_iter(self, p, n):
        x, y = p
        return self.window_iter((x - n, y - n), (x + n + 1, y + n +1))

    ## @brief Returns an iterator that iterates over all cells in the square
    ## surrounding the given point.
    ##
    #The square is 2*n + 1 units.
    # The iterator wraps over the grid. For example, if x is one unit too high
    # (it is outside the grid to the right), the iterator will return first
    # cell in that row.
    def wrapped_square_iter(self, p, n):
        x, y = p
        return self.wrapped_window_iter((x - n, y - n), (x + n + 1, y + n +1))

    ## @brief Returns an iterator that iterates over the indeces of this
    ## grid as tuples.
    ##
    # If grid is a 2 by 2 grid, then:
    # @code
    # for p in index_iter(grid):
    #     print p
    # @endcode
    # will produce
    # @code
    # 0, 0
    # 0, 1
    # 1, 0
    # 1, 1
    # @endcode
    # This iterator is useful for assigning elements of grids:
    # @code
    # for p in index_iter(grid):
    #     grid[p] = random()
    # @endcode
    def index_iter(self):
        for i in xrange(self.width):
            for j in xrange(self.height):
                yield i, j
        raise StopIteration

## Class that implements __str__ and __iter__.
class Container3D (Container):
    def __init__(self, width, height, depth):
        Container.__init__(self, (width, height, depth))
        self.width = width
        self.height = height
        self.depth = depth

        self.__clonetype__ = Grid3D

    def __str__(self):
        #slow...
        glst = []

        for i in xrange(self.width):
            gcol = []
            for j in xrange(self.height):
                gslice = []

                for k in xrange(self.depth):
                    gslice.append(self[i, j, k])

                gcol.append(gslice)
            glst.append(gcol)

        return glst.__repr__()

    def __iter__(self):
        for i in xrange(self.width):
            yield self[i, ..., ...]
        raise StopIteration

    ## Returns an iterator that iterates over all cells in the grid
    def cell_iter(self):
        for i in xrange(self.width):
            for j in xrange(self.height):
                for k in xrange(self.depth):
                    yield self[i, j, k]
        raise StopIteration

    ## @brief Returns an iterator that iterates over the indeces of this
    ## grid as tuples.
    ##
    # If grid is a 2 by 2 grid, then:
    # @code
    # for p in index_iter(grid):
    #     print p
    # @endcode
    # will produce
    # @code
    # 0, 0
    # 0, 1
    # 1, 0
    # 1, 1
    # @endcode
    # This iterator is useful for assigning elements of grids:
    # @code
    # for p in index_iter(grid):
    #     grid[p] = random()
    # @endcode
    def index_iter(self):
        for i in xrange(self.width):
            for j in xrange(self.height):
                for k in xrange (self.depth):
                    yield i, j, k
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y, z in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    # z0 <= z < z1
    #@endcode
    #
    def window_iter(self, p0, p1):
        x0, y0, z0 = p0
        x1, y1, z1 = p1

        for i in xrange(max(0, x0), min(x1, self.width)):
            for j in xrange(max(0, y0), min(y1, self.height)):
                for k in xrange(max(0, z0), min(z1, self.depth)):
                    yield self[i, j, k]
        raise StopIteration

    ## @brief Returns an iterator that iterates over a subgrid of this grid.
    ##
    # The iterator will iterate over all cells x, y, z in the grid
    # such that
    #@code
    # x0 <= x < x1
    # y0 <= y < y1
    # z0 <= z < z1
    #@endcode
    #wrapping around the edges as necessary.

    def wrapped_window_iter(self, p0, p1):
        x0, y0, z0 = p0
        x1, y1, z1 = p1

        for i in xrange(x0, x1):
            for j in xrange(y0, y1):
                for k in xrange(z0, z1):
                    yield self[i % self.width, j % self.height, k % self.depth]
        raise StopIteration
    ## @brief Returns an iterator that iterates over all cells in the square
    ##    surrounding the given point.
    ##
    #The cube is 2*n + 1 units.
    def square_iter(self, p, n):
        x, y, z = p
        return self.window_iter(
            (x - n, y - n, z - n),
            (x + n + 1, y + n +1, z + n +1))
    ## @brief Returns an iterator that iterates over all cells in the square
    ##    surrounding the given point, wrapping around as necessary.
    ##
    #The cube is 2*n + 1 units.
    def wrapped_square_iter(self, p, n):
        x, y, z = p
        return self.wrapped_window_iter(
            (x - n, y - n, z - n),
            (x + n + 1, y + n +1, z + n +1))

class GridWindow1D (Container1D, AuxiliaryContainer): #Constant y
    def __init__(self, grid, col_slice):
        self.grid = grid
        self.x = complete_slice(col_slice, grid.width)
        Container1D.__init__(self, slice_len(self.x), grid.width)

    def __getitem__(self, x):
        new_x = slice_mul(self.x, x, self.grid.width)

        return self.grid[new_x]

    def __setitem__(self, x, item):
        new_x = slice_mul(self.x, x, self.grid.width)

        if type(x) is int:
            self.grid[new_x] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                self.grid[i] = item_i

## Class that represent a 2D grid, with enhanced slicing notation.
class Grid1D (Container1D, PrincipleContainer):
    def __init__(self, dims, initial_item = None):
        (width,) = dims
        Container1D.__init__(self, width)
        self.grid = make_grid_1d(width, initial_item)
        self.width = width
    ## @brief Returns an iterator that iterates over all cells in the grid.
    ##
    # This allows you to write:
    #@code
    # for cell in cell_iter(grid):
    #   process(cell)
    #@endcode
    def cell_iter(self):
        for i in xrange(self.width):
            yield self.grid[i]
        raise StopIteration

    def __getitem__(self, x):
        if isinstance(x, int):
            return self.grid[x]
        elif is_slice(x):
            GridWindow1D(self, x)
        raise TypeError

    def __setitem__(self, x, item):
        if type(x) is int:
            self.grid[x] = item
        elif is_slice(x):
            g = GridWindow1D(self, x)
            g[...] = item
        else:
            raise TypeError

class GridRow2D (Container1D, AuxiliaryContainer): #Constant y
    def __init__(self, grid, col_slice, row):
        self.grid = grid
        self.x = complete_slice(col_slice, grid.width)
        self.y = row

        Container1D.__init__(self, slice_len(self.x, grid.width))

    def __getitem__(self, x):
        new_x = slice_mul(self.x, x, self.grid.width)

        return self.grid[new_x, self.y]

    def __setitem__(self, x, item):
        new_x = slice_mul(self.x, x, self.grid.width)

        if type(x) is int:
            self.grid[new_x] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                self.grid[i, self.y] = item_i

class GridCol2D (Container1D, AuxiliaryContainer): #Constant x
    def __init__(self, grid, col, row_slice):
        self.grid = grid
        self.x = col
        self.y = complete_slice(row_slice, grid.height)

        Container1D.__init__(self, slice_len(self.y, grid.height))

    def __getitem__(self,  y):
        new_y = slice_mul(self.y, y, self.grid.height)

        return self.grid[self.x, new_y]

    def __setitem__(self, y, item):
        new_y = slice_mul(self.y, y, self.grid.height)

        if type(y) is int:
            self.grid[self.x, new_y] = item
        else: #slice!
            for i, item_i in zip(srange(new_y, self.grid.height), item):
                self.grid[self.x, i] = item_i

class GridWindow2D (Container2D):
    def __init__(self, grid, x, y):
        self.grid = grid
        self.x = complete_slice(x, grid.width)
        self.y = complete_slice(y, grid.height)

        Container2D.__init__(self, slice_len(self.x, grid.width), slice_len(self.y, grid.height))

    def __getitem__(self, p):
        if isinstance(p, int):
            return self[p, ...]
        x, y = p

        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)
        return self.grid[new_x, new_y]

    def __setitem__(self, p, item):
        if isinstance(p, int):
            self[p, ...] = item
        x, y = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)

        if type(x) is int or type(y) is int:
            #delegate!
            self.grid[new_x, new_y] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                for j, item_j in zip(srange(new_y, self.grid.height), item_i):
                    self.grid[i, j] = item_j

    def __repr__(self):
        #slow...
        glst = []

        for i in xrange(slice_len(self.x, self.grid.width)):
            gcol = []
            for j in xrange(slice_len(self.y, self.grid.height)):
                gcol.append(self[i, j])
            glst.append(gcol)

        return glst.__repr__()

## Class that represent a 2D grid, with enhanced slicing notation.
class Grid2D (Container2D, PrincipleContainer):

    def __init__(self, dims, initial_item = None):
        (width, height) = dims
        Container2D.__init__(self, width, height)
        self.grid = make_grid_2d(width, height, initial_item)
    ## @brief Returns an iterator that iterates over all cells in the grid.
    ##
    # This allows you to write:
    #@code
    # for cell in cell_iter(grid):
    #   process(cell)
    #@endcode
    def cell_iter(self):
        for i in xrange(self.width):
            for j in xrange(self.height):
                yield self.grid[i][j]
        raise StopIteration

    def __getitem__(self, p):
        if isinstance(p, int):
            return self[p, ...]
        x, y = p
        if isinstance(x, int):
            if isinstance(y, int):
                return self.grid[x][y]
            elif is_slice(y):
                return GridCol2D(self, x, y)
        elif is_slice(x):
            if isinstance(y, int):
                return GridRow2D(self, x, y)
            elif is_slice(y):
                return GridWindow2D(self, x, y)

        raise TypeError

    def __setitem__(self, p, item):
        x, y = p
        if type(x) is int:
            if type(y) is int:
                self.grid[x][y] = item
            elif is_slice(y):
                g = GridCol2D(self, x, y)
                g[...] = item
        elif is_slice(x):
            if type(y) is int:
                g = GridRow2D(self, x, y)
                g[...] = item
            elif is_slice(y):
                g = GridWindow2D(self, x, y)
                g[..., ...] = item
        else:
            raise TypeError

class GridBar3D (Container1D, AuxiliaryContainer): #constant x, y
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = x
        self.y = y
        self.z = complete_slice(z, grid.depth)

        Container1D.__init__(self, slice_len(self.z, grid.depth))

    def __getitem__(self,  z):
        new_z = slice_mul(self.z, z, self.grid.depth)
        return self.grid[self.x, self.y, new_z]

    def __setitem__(self, z, item):
        new_z = slice_mul(self.z, z, self.grid.depth)

        if type(z) is int:
            self.grid[new_z] = item
        else: #slice!
            for i, item_i in zip(srange(new_z, self.grid.depth), item):
                self.grid[self.x, self.y, i] = item_i

class GridCol3D (Container1D, AuxiliaryContainer): #constant x, z
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = x
        self.y = complete_slice(y, grid.height)
        self.z = z

        Container1D.__init__(self, slice_len(self.y, grid.height))

    def __getitem__(self,  y):
        new_y = slice_mul(self.y, y, self.grid.height)
        return self.grid[self.x, new_y, self.z]
    def __setitem__(self, y, item):
        new_y = slice_mul(self.y, y, self.grid.height)

        if type(y) is int:
            self.grid[new_y] = item
        else: #slice!
            for i, item_i in zip(srange(new_y, self.grid.height), item):
                self.grid[self.x, i, self.z] = item_i

class GridRow3D (Container1D, AuxiliaryContainer): #constant y, z
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = complete_slice(x, grid.width)
        self.y = y
        self.z = z

        Container1D.__init__(self, slice_len(self.x, grid.width))
    def __getitem__(self,  x):
        new_x = slice_mul(self.x, x, self.grid.width)
        return self.grid[new_x, self.y, self.z]
    def __setitem__(self, x, item):
        new_x = slice_mul(self.x, x, self.grid.width)

        if type(x) is int:
            self.grid[new_x] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                self.grid[i, self.y, self.y] = item_i

class GridSliceXY (Container2D, AuxiliaryContainer): #constant z
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = complete_slice(x, grid.width)
        self.y = complete_slice(y, grid.height)
        self.z = z
        Container2D.__init__(self, slice_len(self.x, grid.width), slice_len(self.y, grid.height))
    def __getitem__(self,  p):
        if isinstance(p, int):
            return self[p, ...]
        x, y = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)
        return self.grid[new_x, new_y, self.z]

    def __setitem__(self, p, item):
        if isinstance(p, int):
            self[p, ...] = item
        x, y = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)

        if type(x) is int or type(y) is int:
            #delegate!
            self.grid[new_x, new_y, self.z] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                for j, item_j in zip(srange(new_y, self.grid.height), item_i):
                    self.grid[i, j, self.z] = item_j

class GridSliceXZ (Container2D, AuxiliaryContainer): #constant Y
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = complete_slice(x, grid.width)
        self.y = y
        self.z = complete_slice(z, grid.depth)

        Container2D.__init__(self, slice_len(self.x, grid.width), slice_len(self.z, grid.depth))
    def __getitem__(self,  p):
        if isinstance(p, int):
            return self[p, ...]
        x, z = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_z = slice_mul(self.z, z, self.grid.depth)
        return self.grid[new_x, self.y, new_z]

    def __setitem__(self, p, item):
        if isinstance(p, int):
            return self[p, ...]
        x, z = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_z = slice_mul(self.z, z, self.grid.depth)

        if type(x) is int or type(z) is int:
            #delegate!
            self.grid[new_x, self.y, new_z] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                for j, item_j in zip(srange(new_z, self.grid.depth), item_i):
                    self.grid[i, self.y, j] = item_j

class GridSliceYZ (Container2D, AuxiliaryContainer): #constant x
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = x
        self.y = complete_slice(y, grid.height)
        self.z = complete_slice(z, grid.depth)

        Container2D.__init__(self, slice_len(self.y, grid.height), slice_len(self.z, grid.depth))
    def __getitem__(self,  p):
        if isinstance(p, int):
            return self[p, ...]
        y, z = p
        new_y = slice_mul(self.y, y, self.grid.height)
        new_z = slice_mul(self.z, z, self.grid.depth)
        return self.grid[self.x, new_y, new_z]

    def __setitem__(self, p, item):
        if isinstance(p, int):
            self[p, ...] = item
        y, z = p
        new_y = slice_mul(self.y, y, self.grid.height)
        new_z = slice_mul(self.z, z, self.grid.depth)

        if type(y) is int or type(z) is int:
            #delegate!
            self.grid[self.x, new_y, new_z] = item
        else: #slice!
            for i, item_i in zip(srange(new_y), item):
                for j, item_j in zip(srange(new_z), item_i):
                    self.grid[self.x, i, j] = item_j

class GridWindow3D (Container3D, AuxiliaryContainer):
    def __init__(self, grid, x, y, z):
        self.grid = grid
        self.x = complete_slice(x, grid.width)
        self.y = complete_slice(y, grid.height)
        self.z = complete_slice(z, grid.height)

        Container3D.__init__(self, slice_len(self.x, grid.width), slice_len(self.y, grid.height), slice_len(self.z, grid.depth))
    def __getitem__(self, p):
        if isinstance(p, int):
            return self[p, ..., ...]
        x, y, z = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)
        new_z = slice_mul(self.z, z, self.grid.depth)

        return self.grid[new_x, new_y, new_z]

    def __setitem__(self, p, item):
        if isinstance(p, int):
            self[p, ..., ...] = item
        x, y, z = p
        new_x = slice_mul(self.x, x, self.grid.width)
        new_y = slice_mul(self.y, y, self.grid.height)
        new_z = slice_mul(self.z, z, self.grid.depth)

        if type(x) is int or type(y) is int or type(z) is int:
            #delegate!
            self.grid[new_x, new_y, self.z] = item
        else: #slice!
            for i, item_i in zip(srange(new_x, self.grid.width), item):
                for j, item_j in zip(srange(new_y, self.grid.height), item_i):
                    for k, item_k in zip(srange(new_z, self.grid.depth), item_j):
                        self.grid[i, j, k] = item_k

## Class that represent a 3D grid, with enhanced slicing notation.
class Grid3D (Container3D, PrincipleContainer):
    def __init__(self, dims, initial_item = None):
        (width, height, depth) = dims
        Container3D.__init__(self, width, height, depth)
        self.grid = make_grid_3d(width, height, depth, initial_item)

    def __getitem__(self, p):
        if isinstance(p, int):
            return self[p, ..., ...]
        x, y, z = p
        if type(x) is int:
            if type(y) is int:
                if type(z) is int:
                    return self.grid[x][y][z]
                elif is_slice(z):
                    return GridBar3D(self, x, y, z)
            elif is_slice(y):
                if type(z) is int:
                    return GridCol3D(self, x, y, z)
                elif is_slice(z):
                    return GridSliceYZ(self, x, y, z)
        elif is_slice(x):
            if type(y) is int:
                if type(z) is int:
                    return GridRow3D(self, x, y, z)
                elif is_slice(z):
                    return GridSliceXZ(self, x, y, z)
            elif is_slice(y):
                if type(z) is int:
                    return GridSliceXY(self, x, y, z)
                elif is_slice(z):
                    return GridWindow3D(self, x, y, z)

        raise TypeError

    def __setitem__(self, p, item):
        (x, y, z) = p
        if type(x) is int:
            if type(y) is int:
                if type(z) is int:
                    self.grid[x][y][z] = item
                elif is_slice(z):
                    g = GridBar3D(self, x, y, z)
                    g[...] = item
            elif is_slice(y):
                if type(z) is int:
                    g = GridCol3D(self, x, y, z)
                    g[...] = item
                elif is_slice(z):
                    g = GridSliceYZ(self, x, y, z)
                    g[..., ...] = item
        elif is_slice(x):
            if type(y) is int:
                if type(z) is int:
                    g = GridRow3D(self, x, y, z)
                    g[...] = item
                elif is_slice(z):
                    g = GridSliceXZ(self, x, y, z)
                    g[..., ...] = item
            elif is_slice(y):
                if type(z) is int:
                    g = GridSliceXY(self, x, y, z)
                    g[..., ...] = item
                elif is_slice(z):
                    g = GridWindow3D(self, x, y, z)
                    g[..., ..., ...] = item
        else:
            raise TypeError

class ListGrid3D (Grid3D):
    def __init__(self, dims):
        (width, height, depth) = dims
        Grid3D.__init__(self, (width, height, depth))

        for index in self.index_iter():
            self[index] = []

    ## @brief Sets the item at x, y.
    ##
    # Use a tuplet (x, y) to access the item.
    def additem(self, p, b):
        (x, y, z) = p
        self[x, y, z].append(b)

class ListGrid2D (Grid2D):
    def __init__(self, dims):
        (width, height) = dims
        Grid2D.__init__(self, (width, height))

        for index in self.index_iter():
            self[index] = []

    ## @brief Sets the item at x, y.
    ##
    # Use a tuplet (x, y) to access the item.
    def additem(self, p, b):
        (x, y) = p
        self[x, y].append(b)


import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import base64
import json
import os
import matplotlib.cm
import matplotlib.colors as mcolors
import numpy as np
import random
import plotly.graph_objects as go
import plotly.express as px
from skimage import data, transform

COLORMAP = 'plasma'
KEYPOINTS = ['Nose', 'L_Eye', 'R_Eye', 'L_Ear', 'R_Ear', 'Throat',
             'Withers', 'TailSet', 'L_F_Paw', 'R_F_Paw', 'L_F_Wrist',
             'R_F_Wrist', 'L_F_Elbow', 'R_F_Elbow', 'L_B_Paw', 'R_B_Paw',
             'L_B_Hock', 'R_B_Hock', 'L_B_Stiffle', 'R_B_Stiffle']
N_SUBSET = 3

img = data.chelsea()
img = img[::2, ::2]
images = [img, img[::-1], transform.rotate(img, 30)]
cmap = matplotlib.cm.get_cmap(COLORMAP, N_SUBSET)

def make_figure_image(i):
    fig = px.imshow(images[i % len(images)])
    fig.layout.xaxis.showticklabels = False
    fig.layout.yaxis.showticklabels = False
    fig.update_traces(hoverinfo='none', hovertemplate='')
    return fig

def draw_circle(center, radius, n_points=50):
    pts = np.linspace(0, 2 * np.pi, n_points)
    x = center[0] + radius * np.cos(pts)
    y = center[1] + radius * np.sin(pts)
    path = 'M ' + str(x[0]) + ',' + str(y[1])
    for k in range(1, x.shape[0]):
        path += ' L ' + str(x[k]) + ',' + str(y[k])
    path += ' Z'
    return path

def compute_circle_center(path):
    """
    See Eqn 1 & 2 pp.12-13 in REGRESSIONS CONIQUES, QUADRIQUES
    Régressions linéaires et apparentées, circulaire, sphérique
    Jacquelin J., 2009.
    """
    coords = [list(map(float, coords.split(','))) for coords in path.split(' ')[1::2]]
    x, y = np.array(coords).T
    n = len(x)
    sum_x = np.sum(x)
    sum_y = np.sum(y)
    sum_x2 = np.sum(x * x)
    sum_y2 = np.sum(y * y)
    delta11 = n * np.dot(x, y) - sum_x * sum_y
    delta20 = n * sum_x2 - sum_x ** 2
    delta02 = n * sum_y2 - sum_y ** 2
    delta30 = n * np.sum(x ** 3) - sum_x2 * sum_x
    delta03 = n * np.sum(y ** 3) - sum_y * sum_y2
    delta21 = n * np.sum(x * x * y) - sum_x2 * sum_y
    delta12 = n * np.sum(x * y * y) - sum_x * sum_y2

    # Eqn 2, p.13
    num_a = (delta30 + delta12) * delta02 - (delta03 + delta21) * delta11
    num_b = (delta03 + delta21) * delta20 - (delta30 + delta12) * delta11
    den = 2 * (delta20 * delta02 - delta11 * delta11)
    a = num_a / den
    b = num_b / den
    return a, b

def get_plotly_color(n):
    return mcolors.to_hex(cmap(n))

fig = make_figure_image(0)

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

options = random.sample(KEYPOINTS, N_SUBSET)

styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}

app.layout = html.Div([
    html.Div([
        dcc.Graph(
            id='canvas',
            config={'editable': True},
            figure=fig)
    ],
        className="six columns"
    ),
    html.Div([
        html.H2("Controls"),
        dcc.RadioItems(id='radio',
                       options=[{'label': opt, 'value': opt} for opt in options],
                       value=options[0]
                       ),
        html.Button('Previous', id='previous'),
        html.Button('Next', id='next'),
        html.Button('Clear', id='clear'),
        html.Button('Save', id='save'),
        dcc.Store(id='store', data=0),
        html.P([
            html.Label('Keypoint label size'),
            dcc.Slider(id='slider',
                       min=3,
                       max=36,
                       step=1,
                       value=12)
            ], style={'width': '80%',
                      'display': 'inline-block'})
    ],
        className="six columns"
    ),
    html.Div([
        dcc.Markdown("""
                **Instructions**\n
                Click on the image to add a keypoint.
            """),
        html.Pre(id='click-data', style=styles['pre']),
    ],
        className='six columns'
    ),
    html.Div(id='placeholder', style={'display': 'none'}),
    html.Div(id='shapes', style={'display': 'none'})
]
)

@app.callback(Output('placeholder', 'children'),
              [Input('save', 'n_clicks')],
              [State('store', 'data')])
def save_data(click_s, ind_image):
    if click_s:
        xy = {shape.name: compute_circle_center(shape.path) for shape in fig.layout.shapes}
        print(xy, ind_image)

@app.callback(
    [Output('canvas', 'figure'),
     Output('radio', 'value'),
     Output('store', 'data'),
     Output('shapes', 'children')],
    [Input('canvas', 'clickData'),
     Input('canvas', 'relayoutData'),
     Input('next', 'n_clicks'),
     Input('previous', 'n_clicks'),
     Input('clear', 'n_clicks'),
     Input('slider', 'value')],
    [State('canvas', 'figure'),
     State('radio', 'value'),
     State('store', 'data'),
     State('shapes', 'children')]
    )
def update_image(clickData, relayoutData, click_n, click_p, click_c, slider_val,
                 figure, option, ind_image, shapes):
    if not any(event for event in (clickData, click_n, click_p, click_c)):
        return dash.no_update, dash.no_update, dash.no_update, dash.no_update

    if ind_image is None:
        ind_image = 0

    if shapes is None:
        shapes = []
    else:
        shapes = json.loads(shapes)
    n_bpt = options.index(option)

    ctx = dash.callback_context
    button_id = ctx.triggered[0]['prop_id'].split('.')[0]
    if button_id == 'clear':
        fig.layout.shapes = []
        return make_figure_image(ind_image), options[0], ind_image, '[]'
    elif button_id == 'next':
        ind_image = (ind_image + 1) % len(images)
        return make_figure_image(ind_image), options[0], ind_image, '[]'
    elif button_id == 'previous':
        ind_image = (ind_image - 1) % len(images)
        return make_figure_image(ind_image), options[0], ind_image, '[]'
    elif button_id == 'slider':
        for i in range(len(shapes)):
            center = compute_circle_center(shapes[i]['path'])
            new_path = draw_circle(center, slider_val)
            shapes[i]['path'] = new_path

    already_labeled = [shape['name'] for shape in shapes]
    key = list(relayoutData)[0]
    if option not in already_labeled and button_id != 'slider':
        if clickData:
            x, y = clickData['points'][0]['x'], clickData['points'][0]['y']
            circle = draw_circle((x, y), slider_val)
            color = get_plotly_color(n_bpt)
            shape = dict(type='path',
                         path=circle,
                         line_color=color,
                         fillcolor=color,
                         layer='above',
                         opacity=0.8,
                         name=option)
            shapes.append(shape)
    else:
        if 'path' in key and button_id != 'slider':
            ind_moving = int(key.split('[')[1].split(']')[0])
            path = relayoutData.pop(key)
            shapes[ind_moving]['path'] = path
    fig.update_layout(shapes=shapes)
    if 'range[' in key:
        xrange = relayoutData['xaxis.range[0]'], relayoutData['xaxis.range[1]']
        yrange = relayoutData['yaxis.range[0]'], relayoutData['yaxis.range[1]']
        fig.update_xaxes(range=xrange, autorange=False)
        fig.update_yaxes(range=yrange, autorange=False)
    elif 'autorange' in key:
        fig.update_xaxes(autorange=True)
        fig.update_yaxes(autorange=True)
    if button_id != 'slider':
        n_bpt += 1
    new_option = options[min(len(options) - 1, n_bpt)]
    return ({'data': figure['data'], 'layout': fig['layout']},
            new_option,
            ind_image,
            json.dumps(shapes))

if __name__ == '__main__':
    app.run_server(debug=False, port=8051)


from __future__ import print_function
import tensorflow as tf
import numpy as np

def test(tf_func, py_func):
    print('func', tf_func)
    with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
        with tf.device('/gpu:0'):
            tf_a = tf.placeholder(tf.float32, [None, None], 'a')
            tf_c = tf.__dict__[tf_func](tf_a, name="c")

        np.random.seed(123)
        shape = (1, 10)
        a = np.random.choice(50, shape) / 50
        if 'sqrt' not in tf_func and 'log' not in tf_func:
            a -= 0.5

        ar, cr = sess.run((tf_a, tf_c), {tf_a: a})
        print('ar', ar)
        print('cr', cr)
        c_py = eval(py_func)
        diff = np.abs(c_py - cr).max()
        print('diff', diff)
        assert diff < 1e-4, 'failed for %s' % tf_func

funcs = {
    'tanh': 'np.tanh(a)',
    'neg': 'np.negative(a)',
    'exp': 'np.exp(a)',
    # 'sigmoid': '1/(1+np.exp(-a))',
    'sqrt': 'np.sqrt(a)',
    'log': 'np.log(a)',
    'abs': 'np.abs(a)',
    'floor': 'np.floor(a)',
    'ceil': 'np.ceil(a)',
    'square': 'np.square(a)'
}
for tf_func, py_func in funcs.items():
    test(tf_func, py_func)


# -- coding: utf-8 --
def sal():
    N=int(input())
    summ=1
    u=1
    z=0
    for i in range(3,N+1):
        u+=z
        summ+=u
        z=u-z
    print(summ)
sal()

практика 3/Задание 9.py

Задание 9.py


import cs50
import sys

#ASCII constants for UPPERCASE/lowercase strings:
TO_LOWER = 97
TO_UPPER = 65
ALPHABET_SIZE = 26

def main():
    if len(sys.argv) != 2:
        print("Invalid arguments provided")
        exit(1)

    key = int(sys.argv[1])
    print("plaintext: ", end = "")
    cipher_text = cs50.get_string()
    print("ciphertext: ", end = "")
    translated_text = []

    for char in cipher_text:
        if (cipher_text.isalpha() and cipher_text.isupper()):
            translated_text.append(chr(((ord(char) - TO_UPPER + key) % ALPHABET_SIZE) + TO_UPPER))
        elif (cipher_text.isalpha()):
             translated_text.append(chr(((ord(char) - TO_LOWER + key) % ALPHABET_SIZE) + TO_LOWER))
        else:
            translated_text.append(char)
    print ("".join(translated_text))

if __name__ == "__main__":
    main()


numbers = list(range(1, 101))
for num in numbers:
    if num % 15 == 0:
        print("FizzBuzz")
    elif num % 5 == 0:
        print("Buzz")
    elif num % 3 == 0:
        print("Fizz")
    else:
        print(num)


import requests
import re
from libraries.emojiflags.lookup import lookup
from config import config
from lang import lang
from clients.telegram_client import telegramClient

class WeatherbitClient():
    def getWeather(self, request):
        rq = requests.get(request)
        if rq.status_code == requests.codes.ok:
            data = rq.json()['data'][0]
            code = data['weather']['code']
            description = data['weather']['description']
            city_name = data['city_name']
            country_code = data['country_code']
            wind_spd = data['wind_spd']
            wind_dir = data['wind_cdir_full']
            temp = data['temp']
            temp_feels_like = data['app_temp']
            cloud_percentage = data['clouds']
            humidity_percentage = data['rh']
            precip = data['precip']

            message = f'{city_name} {lookup(country_code)}\n'
            message += f'{self.weatherEmoji(code)} {description}\n'
            message += '🌡 {:.2f}º, se siente como {:.2f}º\n'.format(
                temp, temp_feels_like)
            message += '{}: {:.2f}m/s {}\n'.format(lang.weatherbit_messages['wind'], wind_spd, wind_dir)
            message += '{}: {:.2f}%\n'.format(lang.weatherbit_messages['clouds'], cloud_percentage)
            message += '{}: {:.2f}\n'.format(lang.weatherbit_messages['rainfall'], precip)
            message += '{:.2f}% {}\n'.format(humidity_percentage, lang.weatherbit_messages['air_humidity'])
            telegramClient.sendMessage(message)
        else:
            telegramClient.sendMessage(lang.weatherbit_messages['weatherbit_not_responding'])

    def weatherRequestHandeler(self, argument):
        if re.search('[0-9]+', argument.upper()):
            request = '{}&postal_code={}&country={}'.format(
                config.weatherbit_url, argument, config.weatherbit_api_country)
            self.getWeather(request)
        elif re.search('[A-z ]+(, [A-z]+).', argument.upper()):
            arguments = argument.split(', ')
            request = '{}&city={}&country={}'.format(
                config.weatherbit_url, arguments[0], arguments[1])
            print(request)
            self.getWeather(request)
        elif re.search('[A-z ]+', argument.upper()):
            request = '{}&city={}'.format(config.weatherbit_url, argument)
            self.getWeather(request)

    def weatherEmoji(self, code):
        if code >= 200 and code <= 202:
            return lang.weatherbit_messages['code_to_emoji']['200_202']
        if code >= 230 and code <= 233:
            return lang.weatherbit_messages['code_to_emoji']['230_233']
        if code >= 300 and code <= 302:
            return lang.weatherbit_messages['code_to_emoji']['300_302']
        if code >= 500 and code <= 522:
            return lang.weatherbit_messages['code_to_emoji']['500_522']
        if code >= 600 and code <= 623:
            return lang.weatherbit_messages['code_to_emoji']['600_623']
        if code >= 700 and code <= 741:
            return lang.weatherbit_messages['code_to_emoji']['700_741']
        if code == 800:
            return lang.weatherbit_messages['code_to_emoji']['800']
        if code >= 801 and code <= 802:
            return lang.weatherbit_messages['code_to_emoji']['801_802']
        if code == 803:
            return lang.weatherbit_messages['code_to_emoji']['803']
        if code == 804:
            return lang.weatherbit_messages['code_to_emoji']['804']
        if code == 900:
            return lang.weatherbit_messages['code_to_emoji']['900']

weatherbitClient = WeatherbitClient()


from __future__ import division
from builtins import str
from builtins import range

from sciunit import Score
import numpy
from sciunit.utils import assert_dimensionless
import collections

class ZScore_PathwayInteraction(Score):
    """
    Average of Z scores. A float indicating the average of standardized difference
    from reference means for Pathway Interaction features.
    """

    def __init__(self, score, related_data={}):

        if not isinstance(score, Exception) and not isinstance(score, float):
            raise InvalidScoreError("Score must be a float.")
        else:
            super(ZScore_PathwayInteraction,self).__init__(score, related_data=related_data)

    @classmethod
    def compute(cls, observation, prediction):
        """Computes average of z-scores from observation and prediction for Pathway Interaction features"""

        errors_dict = collections.OrderedDict()
        errors=[]

        for pathway, value in prediction.items():
            if pathway not in list(errors_dict.keys()):
                errors_dict[pathway] = {}
            for feat_name, feat_value in prediction[pathway].items():
                p_mean = prediction[pathway][feat_name]['mean']
                o_mean = observation[pathway][feat_name]['mean']
                o_std = observation[pathway][feat_name]['std']

                try:
                    feature_error = abs(p_mean - o_mean)/o_std
                    feature_error = assert_dimensionless(feature_error)

                except (TypeError,AssertionError) as e:
                    feature_error = e
                errors.append(feature_error)
                errors_dict[pathway].update({feat_name : feature_error})

        penalty_PP_depol = 0
        penalty_SC_PP = 0
        if numpy.isnan(errors_dict['PP+depol']['plateau duration']):
            penalty_PP_depol += 100
        if numpy.isnan(errors_dict['SC+PP']['plateau duration']):
            penalty_SC_PP += 100

        score_avg=numpy.nanmean(errors) + penalty_PP_depol + penalty_SC_PP

        return score_avg, errors_dict, penalty_PP_depol, penalty_SC_PP

    def __str__(self):

        return 'ZScore_avg = %.2f' % self.score


import os
from collections import OrderedDict as odict
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F

import sys
from dataset.data_utils import rotation_err

def adjust_learning_rate(optimizer, base_lr):
    """Sets the learning rate to the initial LR decayed by 10"""
    lr = base_lr * 0.1
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr
    return lr

def freeze_model(model):
    for m in model.modules():
        for p in m.parameters(): p.requires_grad = False

def freeze_bn(model):
    for m in model.modules():
        if isinstance(m, nn.BatchNorm2d):
            for p in m.parameters(): p.requires_grad = False

def save_checkpoint(state, filename):
    """save checkpoint"""
    torch.save(state, filename)

def load_checkpoint(model, pth_file, check=False):
    """load state and network weights"""
    checkpoint = torch.load(pth_file, map_location=lambda storage, loc: storage.cuda())
    if 'model' in checkpoint.keys():
        pretrained_dict = checkpoint['model']
    else:
        pretrained_dict = checkpoint['state_dict']
    model_dict = model.state_dict()

    if check:
        print(len(model_dict.keys()), len(pretrained_dict.keys()))
        missed = [name for name in model_dict.keys() if name not in pretrained_dict.keys()]
        print(missed)

    pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict and 'fc' not in k}
    model_dict.update(pretrained_dict)
    model.load_state_dict(model_dict)
    print('Pre-trained model weight loaded')

def get_pred_from_cls_output(outputs):
    preds = []
    for n in range(0, len(outputs)):
        output = outputs[n]
        _, pred = output.topk(1, 1, True, True)
        preds.append(pred.view(-1))
    return preds

# classification loss
CE = nn.CrossEntropyLoss().cuda()

class LabelSmoothingLoss(nn.Module):
    def __init__(self, classes, smoothing=0.0, dim=-1):
        super(LabelSmoothingLoss, self).__init__()
        self.confidence = 1.0 - smoothing
        self.smoothing = smoothing
        self.cls = classes
        self.dim = dim

    def forward(self, pred, target):
        pred = pred.log_softmax(dim=self.dim)
        with torch.no_grad():
            # true_dist = pred.data.clone()
            true_dist = torch.zeros_like(pred)
            true_dist.fill_(self.smoothing / (self.cls - 1))
            true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
        return torch.mean(torch.sum(-true_dist * pred, dim=self.dim))

class SmoothCELoss(nn.Module):
    def __init__(self, range, classes, smooth=0.):
        super(SmoothCELoss, self).__init__()
        self.__range__ = range
        self.__smooth__ = smooth
        self.__SmoothLoss__ = LabelSmoothingLoss(classes, smoothing=smooth, dim=-1)

    def forward(self, pred, target):
        binSize = self.__range__ // pred.size(1)
        trueLabel = target // binSize
        return self.__SmoothLoss__(pred, trueLabel)

def cross_entropy_loss(pred, target, range):
    binSize = range // pred.size(1)
    trueLabel = target // binSize
    return CE(pred, trueLabel)

class ClsLoss(nn.Module):
    def __init__(self, range):
        super(ClsLoss, self).__init__()
        self.__range__ = range
        return

    def forward(self, pred, target):
        return cross_entropy_loss(pred, target, self.__range__)

# regression loss
Huber = nn.SmoothL1Loss().cuda()

def delta_loss(pred_azi, pred_ele, pred_rol, target, bin):
    # compute the ground truth delta value according to angle value and bin size
    target_delta = (target % bin) / bin

    # compute the delta prediction in the ground truth bin
    target_label = (target // bin).long()
    delta_azi = pred_azi[torch.arange(pred_azi.size(0)), target_label[:, 0]]
    delta_ele = pred_ele[torch.arange(pred_ele.size(0)), target_label[:, 1]]
    delta_rol = pred_rol[torch.arange(pred_rol.size(0)), target_label[:, 2]]
    pred_delta = torch.cat((delta_azi.unsqueeze(1), delta_ele.unsqueeze(1), delta_rol.unsqueeze(1)), 1)

    return Huber(5. * pred_delta, 5. * target_delta)

class DeltaLoss(nn.Module):
    def __init__(self, bin):
        super(DeltaLoss, self).__init__()
        self.__bin__ = bin
        return

    def forward(self, pred_azi, pred_ele, pred_rol, target):
        return delta_loss(pred_azi, pred_ele, pred_rol, target, self.__bin__)

class negDotLoss:
    def __init_(self):
        pass

    def compute_loss(self, tgts, Pred, GT):
        Loss = odict()
        for tgt in tgts:
            Loss[tgt] = torch.mean(-torch.bmm(
                GT[tgt].view(GT[tgt].shape[0], 1, 2).float(),
                Pred[tgt].view(Pred[tgt].shape[0], 2, 1).float()))
        return Loss

class CELoss:
    def __init__(self):
        self.CELoss = nn.CrossEntropyLoss().cuda()

    def compute_loss(self, tgts, Pred, GT):
        Loss = odict()
        for tgt in tgts:
            Loss[tgt] = self.CELoss(Pred[tgt].view(Pred[tgt].size()[0], 4), GT[tgt].view(Pred[tgt].size()[0],))
        return Loss

def infoNCE(feat_ori, feat_pos, tau=0.1):
    # L-2 normalization
    feat_ori = F.normalize(feat_ori, dim=-1)
    feat_pos = F.normalize(feat_pos, dim=-1)
    b = feat_pos.shape[0]

    # logits
    l_pos = torch.einsum('nc,nc->n', [feat_ori, feat_pos]).unsqueeze(-1)  # (N,1)
    l_neg = torch.einsum('nc,ck->nk', [feat_ori, feat_ori.transpose(0, 1)])  # (N, N)
    logits = (1 - torch.eye(b)).type_as(l_neg) * l_neg + torch.eye(b).type_as(l_pos) * l_pos

    # loss
    logits = logits / tau
    labels = torch.arange(b, dtype=torch.long).cuda()
    loss = F.cross_entropy(logits, labels)
    return loss

def poseNCE(feat_ori, feat_pos, label, tau=0.1, weighting="linear"):
    # L-2 normalization
    feat_ori = F.normalize(feat_ori, dim=-1)
    feat_pos = F.normalize(feat_pos, dim=-1)
    feat_all = feat_ori.clone()
    label_all = label.clone()
    b = feat_ori.shape[0]

    # compute pose-distance in adjacency (N, N)
    label_ori_rep = label.reshape(-1, 1, 3).repeat(1, b, 1)
    label_all_rep = label_all.reshape(1, -1, 3).repeat(b, 1, 1)
    dist = rotation_err(label_ori_rep.reshape(-1, 3), label_all_rep.reshape(-1, 3))
    dist = dist.reshape(b, b)

    # rescale the dist from [0, 180] degrees to [0, 1]
    if weighting == 'linear':
        dist = dist / 180
    elif weighting == 'square':
        dist = (dist / 180) ** 2
    elif weighting == 'sqrt':
        dist = torch.sqrt(dist / 180)
    elif weighting == 'sin':
        dist = torch.abs(torch.sin(dist / 180 * np.pi))
    elif weighting == 'sinsin':
        dist = torch.sin(dist / 180 * np.pi) ** 2

    # logites
    l_pos = torch.exp(torch.einsum('nc,nc->n', [feat_ori, feat_pos]).unsqueeze(-1) / tau)  # (N,1)
    l_neg = torch.exp(torch.einsum('nc,ck->nk', [feat_ori, feat_all.transpose(0, 1)]) / tau) * dist  # (N, N)

    logits = torch.cat([l_pos, l_neg], dim=1)

    # loss
    loss = - torch.log(logits[:, 0] / torch.sum(logits, -1))
    return loss.mean()


import os
import argparse
import numpy as np
import pandas as pd
import networkx as nx
from tqdm import tqdm
import multiprocessing
from pathlib import Path
import concurrent.futures
from collections import defaultdict
from itertools import product, permutations
from multiprocessing import Pool, cpu_count
from networkx.convert_matrix import from_pandas_adjacency

def UndirectedFDR(evalObject, algorithmName):
    '''
    Computes empirical FDR for a given algorithm for each dataset.
    This is done for a range of targeted FDRs.
    We define FDR as the fraction of false
    positives among those edges having q less than the targeted FDR,
    where q is an FDR-adjusted p-value.
    Only the column named "q_value" is accepted.
    False positives are judged in an undirected way, so
    regulator --> target and target --> regulator are both "correct".

    :param evalObject: An object of class :class:`BLEval.BLEval`.
    :type evalObject: BLEval

    :param algorithmName: Name of the algorithm for which the early precision is computed.
    :type algorithmName: str

    :returns:
        A dataframe containing targeted and empirical FDR values
        for a given algorithm for each dataset.

    '''
    rankDict = {}
    for dataset in tqdm(evalObject.input_settings.datasets):
        trueEdgesDF = pd.read_csv(str(evalObject.input_settings.datadir)+'/'+ \
                      dataset['name'] + '/' +\
                      dataset['trueEdges'], sep = ',',
                      header = 0, index_col = None)
        trueEdgesDF = trueEdgesDF.loc[(trueEdgesDF['Gene1'] != trueEdgesDF['Gene2'])]
        trueEdgesDF.drop_duplicates(keep = 'first', inplace=True)
        trueEdgesDF.reset_index(drop=True, inplace=True)

        outDir = str(evalObject.output_settings.base_dir) + \
                 str(evalObject.input_settings.datadir).split("inputs")[1] + \
                 "/" + dataset["name"] + "/" + algorithmName

        #algos = evalObject.input_settings.algorithms
        rank_path = outDir + "/outFile.txt"
        if not os.path.isdir(outDir):
            rankDict[dataset["name"]] = set([])
            continue
        try:
            predDF = pd.read_csv(rank_path, sep="\t", header=0, index_col=None)
        except:
            print("\nSkipping fdr computation for ", algorithmName, "on path", outDir)
            rankDict[dataset["name"]] = set([])
            continue

        if not "q_value" in predDF.columns:
            print("\nSkipping fdr computation for ", algorithmName, "on path", outDir)
            rankDict[dataset["name"]] = set([])
            continue

        predDF = predDF.loc[(predDF['Gene1'] != predDF['Gene2'])]
        predDF.drop_duplicates(keep = 'first', inplace=True)
        predDF.reset_index(drop=True, inplace=True)

        # Add reverse edges to judge statistical independence instead of causation
        trueEdges = trueEdgesDF['Gene1'] + "|" + trueEdgesDF['Gene2']
        trueEdges_reversed = trueEdgesDF['Gene2'] + "|" + trueEdgesDF['Gene1']
        trueEdges = set(trueEdges.values).union( set(trueEdges_reversed.values) )
        numEdges = len(trueEdges)

        # check if ranked edges list is empty
        # if so, it is just set to an empty set
        if predDF.shape[0] == 0:
            print("\nSkipping fdr computation for on path ", rank_path,"due to lack of predictions.")
            rankDict[dataset["name"]] = set([])
        else:
            # we want to ensure that we do not include
            # edges without any edge weight
            # so check if the non-zero minimum is
            # greater than the edge weight of the top-kth
            # node, else use the non-zero minimum value.
            def get_candidates(threshold):
                newDF = predDF.loc[(predDF['q_value'] <= threshold)]
                return set(newDF['Gene1'] + "|" + newDF['Gene2'])
            rankDict[dataset["name"]] = [get_candidates(0.1*x) for x in range(10)]
    Fdr = {}

    for dataset in tqdm(evalObject.input_settings.datasets):
        if len(rankDict[dataset["name"]]) != 0:
            def compute_fdr(candidates):
                falseDiscoveries = candidates.difference(trueEdges)
                return len(falseDiscoveries)/max(1, len(candidates))
            Fdr[dataset["name"]] = [compute_fdr(c) for c in rankDict[dataset["name"]]]
        else:
            Fdr[dataset["name"]] = [np.NaN for i in range(10)]

    return(Fdr)


import cv2
import mediapipe as mp
import time
import HandTrackingModule as htm

cap = cv2.VideoCapture(0)
p_time = 0
c_time = 0

detector = htm.HandDetector()

while True:
    success, img = cap.read()
    img = detector.find_hands(img=img)
    lm_list = detector.find_position(img)
    if len(lm_list):
        print(lm_list[4])

    c_time = time.time()
    fps = 1 / (c_time - p_time)
    p_time = c_time

    cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3)
    cv2.imshow("Hand Tracking", img)
    cv2.waitKey(1)


#!/usr/bin/env python

import matplotlib.pyplot as plt
import numpy as np
import time
import cv2
from real.camera import Camera
from robot import Robot
from scipy import optimize
from mpl_toolkits.mplot3d import Axes3D

# User options (change me)
# --------------- Setup options ---------------
tcp_host_ip = '100.127.7.223' # IP and port to robot arm as TCP client (UR5)
tcp_port = 30002
rtc_host_ip = '100.127.7.223' # IP and port to robot arm as real-time client (UR5)
rtc_port = 30003
workspace_limits = np.asarray([[0.3, 0.748], [0.05, 0.4], [-0.2, -0.1]]) # Cols: min max, Rows: x y z (define workspace limits in robot coordinates)
calib_grid_step = 0.05
checkerboard_offset_from_tool = [0,-0.13,0.02]
tool_orientation = [-np.pi/2,0,0] # [0,-2.22,2.22] # [2.22,2.22,0]
# ---------------------------------------------

# Construct 3D calibration grid across workspace
gridspace_x = np.linspace(workspace_limits[0][0], workspace_limits[0][1], 1 + (workspace_limits[0][1] - workspace_limits[0][0])/calib_grid_step)
gridspace_y = np.linspace(workspace_limits[1][0], workspace_limits[1][1], 1 + (workspace_limits[1][1] - workspace_limits[1][0])/calib_grid_step)
gridspace_z = np.linspace(workspace_limits[2][0], workspace_limits[2][1], 1 + (workspace_limits[2][1] - workspace_limits[2][0])/calib_grid_step)
calib_grid_x, calib_grid_y, calib_grid_z = np.meshgrid(gridspace_x, gridspace_y, gridspace_z)
num_calib_grid_pts = calib_grid_x.shape[0]*calib_grid_x.shape[1]*calib_grid_x.shape[2]
calib_grid_x.shape = (num_calib_grid_pts,1)
calib_grid_y.shape = (num_calib_grid_pts,1)
calib_grid_z.shape = (num_calib_grid_pts,1)
calib_grid_pts = np.concatenate((calib_grid_x, calib_grid_y, calib_grid_z), axis=1)

measured_pts = []
observed_pts = []
observed_pix = []

# Move robot to home pose
print('Connecting to robot...')
robot = Robot(False, None, None, workspace_limits,
              tcp_host_ip, tcp_port, rtc_host_ip, rtc_port,
              False, None, None)
robot.open_gripper()

# Slow down robot
robot.joint_acc = 1.4
robot.joint_vel = 1.05

# Make robot gripper point upwards
robot.move_joints([-np.pi, -np.pi/2, np.pi/2, 0, np.pi/2, np.pi])

# Move robot to each calibration point in workspace
print('Collecting data...')
for calib_pt_idx in range(num_calib_grid_pts):
    tool_position = calib_grid_pts[calib_pt_idx,:]
    robot.move_to(tool_position, tool_orientation)
    time.sleep(1)

    # Find checkerboard center
    checkerboard_size = (3,3)
    refine_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    camera_color_img, camera_depth_img = robot.get_camera_data()
    bgr_color_data = cv2.cvtColor(camera_color_img, cv2.COLOR_RGB2BGR)
    gray_data = cv2.cvtColor(bgr_color_data, cv2.COLOR_RGB2GRAY)
    checkerboard_found, corners = cv2.findChessboardCorners(gray_data, checkerboard_size, None, cv2.CALIB_CB_ADAPTIVE_THRESH)
    if checkerboard_found:
        corners_refined = cv2.cornerSubPix(gray_data, corners, (3,3), (-1,-1), refine_criteria)

        # Get observed checkerboard center 3D point in camera space
        checkerboard_pix = np.round(corners_refined[4,0,:]).astype(int)
        checkerboard_z = camera_depth_img[checkerboard_pix[1]][checkerboard_pix[0]]
        checkerboard_x = np.multiply(checkerboard_pix[0]-robot.cam_intrinsics[0][2],checkerboard_z/robot.cam_intrinsics[0][0])
        checkerboard_y = np.multiply(checkerboard_pix[1]-robot.cam_intrinsics[1][2],checkerboard_z/robot.cam_intrinsics[1][1])
        if checkerboard_z == 0:
            continue

        # Save calibration point and observed checkerboard center
        observed_pts.append([checkerboard_x,checkerboard_y,checkerboard_z])
        # tool_position[2] += checkerboard_offset_from_tool
        tool_position = tool_position + checkerboard_offset_from_tool

        measured_pts.append(tool_position)
        observed_pix.append(checkerboard_pix)

        # Draw and display the corners
        # vis = cv2.drawChessboardCorners(robot.camera.color_data, checkerboard_size, corners_refined, checkerboard_found)
        vis = cv2.drawChessboardCorners(bgr_color_data, (1,1), corners_refined[4,:,:], checkerboard_found)
        cv2.imwrite('%06d.png' % len(measured_pts), vis)
        cv2.imshow('Calibration',vis)
        cv2.waitKey(10)

# Move robot back to home pose
robot.go_home()

measured_pts = np.asarray(measured_pts)
observed_pts = np.asarray(observed_pts)
observed_pix = np.asarray(observed_pix)
world2camera = np.eye(4)

# Estimate rigid transform with SVD (from Nghia Ho)
def get_rigid_transform(A, B):
    assert len(A) == len(B)
    N = A.shape[0]; # Total points
    centroid_A = np.mean(A, axis=0)
    centroid_B = np.mean(B, axis=0)
    AA = A - np.tile(centroid_A, (N, 1)) # Centre the points
    BB = B - np.tile(centroid_B, (N, 1))
    H = np.dot(np.transpose(AA), BB) # Dot is matrix multiplication for array
    U, S, Vt = np.linalg.svd(H)
    R = np.dot(Vt.T, U.T)
    if np.linalg.det(R) < 0: # Special reflection case
       Vt[2,:] *= -1
       R = np.dot(Vt.T, U.T)
    t = np.dot(-R, centroid_A.T) + centroid_B.T
    return R, t

def get_rigid_transform_error(z_scale):
    global measured_pts, observed_pts, observed_pix, world2camera, camera

    # Apply z offset and compute new observed points using camera intrinsics
    observed_z = observed_pts[:,2:] * z_scale
    observed_x = np.multiply(observed_pix[:,[0]]-robot.cam_intrinsics[0][2],observed_z/robot.cam_intrinsics[0][0])
    observed_y = np.multiply(observed_pix[:,[1]]-robot.cam_intrinsics[1][2],observed_z/robot.cam_intrinsics[1][1])
    new_observed_pts = np.concatenate((observed_x, observed_y, observed_z), axis=1)

    # Estimate rigid transform between measured points and new observed points
    R, t = get_rigid_transform(np.asarray(measured_pts), np.asarray(new_observed_pts))
    t.shape = (3,1)
    world2camera = np.concatenate((np.concatenate((R, t), axis=1),np.array([[0, 0, 0, 1]])), axis=0)

    # Compute rigid transform error
    registered_pts = np.dot(R,np.transpose(measured_pts)) + np.tile(t,(1,measured_pts.shape[0]))
    error = np.transpose(registered_pts) - new_observed_pts
    error = np.sum(np.multiply(error,error))
    rmse = np.sqrt(error/measured_pts.shape[0]);
    return rmse

# Optimize z scale w.r.t. rigid transform error
print('Calibrating...')
z_scale_init = 1
optim_result = optimize.minimize(get_rigid_transform_error, np.asarray(z_scale_init), method='Nelder-Mead')
camera_depth_offset = optim_result.x

# Save camera optimized offset and camera pose
print('Saving...')
np.savetxt('real/camera_depth_scale.txt', camera_depth_offset, delimiter=' ')
get_rigid_transform_error(camera_depth_offset)
camera_pose = np.linalg.inv(world2camera)
np.savetxt('real/camera_pose.txt', camera_pose, delimiter=' ')
print('Done.')

# DEBUG CODE -----------------------------------------------------------------------------------

# np.savetxt('measured_pts.txt', np.asarray(measured_pts), delimiter=' ')
# np.savetxt('observed_pts.txt', np.asarray(observed_pts), delimiter=' ')
# np.savetxt('observed_pix.txt', np.asarray(observed_pix), delimiter=' ')
# measured_pts = np.loadtxt('measured_pts.txt', delimiter=' ')
# observed_pts = np.loadtxt('observed_pts.txt', delimiter=' ')
# observed_pix = np.loadtxt('observed_pix.txt', delimiter=' ')

# fig = plt.figure()
# ax = fig.add_subplot(111, projection='3d')
# ax.scatter(measured_pts[:,0],measured_pts[:,1],measured_pts[:,2], c='blue')

# print(camera_depth_offset)
# R, t = get_rigid_transform(np.asarray(measured_pts), np.asarray(observed_pts))
# t.shape = (3,1)
# camera_pose = np.concatenate((np.concatenate((R, t), axis=1),np.array([[0, 0, 0, 1]])), axis=0)
# camera2robot = np.linalg.inv(camera_pose)
# t_observed_pts = np.transpose(np.dot(camera2robot[0:3,0:3],np.transpose(observed_pts)) + np.tile(camera2robot[0:3,3:],(1,observed_pts.shape[0])))

# ax.scatter(t_observed_pts[:,0],t_observed_pts[:,1],t_observed_pts[:,2], c='red')

# new_observed_pts = observed_pts.copy()
# new_observed_pts[:,2] = new_observed_pts[:,2] * camera_depth_offset[0]
# R, t = get_rigid_transform(np.asarray(measured_pts), np.asarray(new_observed_pts))
# t.shape = (3,1)
# camera_pose = np.concatenate((np.concatenate((R, t), axis=1),np.array([[0, 0, 0, 1]])), axis=0)
# camera2robot = np.linalg.inv(camera_pose)
# t_new_observed_pts = np.transpose(np.dot(camera2robot[0:3,0:3],np.transpose(new_observed_pts)) + np.tile(camera2robot[0:3,3:],(1,new_observed_pts.shape[0])))

# ax.scatter(t_new_observed_pts[:,0],t_new_observed_pts[:,1],t_new_observed_pts[:,2], c='green')

# plt.show()


import numpy as np

def zeropad_arrays(arrays):
    '''
    Takes a list of arrays, zero-pads all arrays to the right to match longest array in length.
    Returns list of arrays with matched length.
    '''
    padded = []
    max_len = np.max([len(x) for x in arrays])
    for x in arrays:
        padded.append(np.hstack([x, np.zeros(max_len-len(x))]))

    return padded


# -*- coding: utf-8 -*-
"""
Created on Sun Jul 15 01:05:58 2018

@author: Nzix
"""

import binascii
import struct
import base64
import json
import os
import requests
from PIL import Image
from io import StringIO
import shutil
from Crypto.Cipher import AES
# you can directly import EasyID3 and ID3
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, APIC

def dump(file_path):

    core_key = binascii.a2b_hex("687A4852416D736F356B496E62617857")
    meta_key = binascii.a2b_hex("2331346C6A6B5F215C5D2630553C2728")
    unpad = lambda s : s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))]
    try:
        f = open(file_path,'rb')
    except IOError as e:
        print(e.errno)
        print(e)

    # magic header
    header = f.read(8)
    assert binascii.b2a_hex(header) == b'4354454e4644414d'

    # key data
    f.seek(2, 1)
    key_length = f.read(4)
    key_length = struct.unpack('<I', bytes(key_length))[0]

    key_data = bytearray(f.read(key_length))
    key_data = bytes(bytearray([byte ^ 0x64 for byte in key_data]))

    cryptor = AES.new(core_key, AES.MODE_ECB)
    key_data = unpad(cryptor.decrypt(key_data))[17:]
    key_length = len(key_data)

    # key box
    key_data = bytearray(key_data)
    key_box = bytearray(range(256))
    j = 0

    for i in range(256):
        j = (key_box[i] + j + key_data[i % key_length]) & 0xff
        key_box[i], key_box[j] = key_box[j], key_box[i]

    # meta data
    meta_length = f.read(4)
    meta_length = struct.unpack('<I', bytes(meta_length))[0]

    meta_data = bytearray(f.read(meta_length))
    meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data]))
    meta_data = base64.b64decode(meta_data[22:])

    cryptor = AES.new(meta_key, AES.MODE_ECB)
    meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:]
    meta_data = json.loads(meta_data)

    # crc32
    crc32 = f.read(4)
    crc32 = struct.unpack('<I', bytes(crc32))[0]

    # album cover
    f.seek(5, 1)
    image_size = f.read(4)
    image_size = struct.unpack('<I', bytes(image_size))[0]
    image_data = f.read(image_size)

    # media data
    file_name = meta_data['artist'][0][0] + ' - ' + meta_data['musicName'] + '.' + meta_data['format']
    new_file_path = os.path.join(os.path.split(file_path)[0],file_name)
    m = open(new_file_path,'wb')

    http = requests.urllib3.PoolManager()

    with http.request('GET', meta_data['albumPic'], preload_content=False) as r, open('img.jpg', 'wb') as out_file:
        shutil.copyfileobj(r, out_file)
    img = open('img.jpg','rb')

    chunk = bytearray()
    while True:
        chunk = bytearray(f.read(0x8000))
        chunk_length = len(chunk)
        if not chunk:
            break

        for i in range(chunk_length):
            j = (i + 1) & 0xff
            chunk[i] ^= key_box[(key_box[j] + key_box[(key_box[j] + j) & 0xff]) & 0xff]

        m.write(chunk)

    m.close()
    f.close()

    # add music tag
    audio = EasyID3(new_file_path)
    audio['artist'] = meta_data['artist'][0][0]
    audio['title'] = meta_data['musicName']
    audio['album'] = meta_data['album']
    audio.save()

    audio = ID3(new_file_path)
    audio['APIC'] = APIC(
                          encoding=3,
                          mime='image/jpeg',
                          type=3, desc=u'Cover',
                          data=img.read()
                        )
    audio.save()

if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        file_path = sys.argv[1:]
        directory = os.fsencode(file_path[0])
        for file in os.listdir(directory):
            filename = os.fsdecode(file)
            if filename.endswith(".ncm"):
                try:
                    dump(os.path.join(file_path[0], filename))
                except Exception as e:
                    print(e)
    else:
        print('please input file path!')


"""
We will create some functions in this file which will make our work easier.
"""

async def check_guild(guild_id: int, discord):
    """A helper function to check if the guild is a proper guild to work with"""
    user_guilds = await discord.fetch_guilds()
    guild = None
    for each_guild in user_guilds:
        if int(each_guild.id) == int(guild_id):
            guild = each_guild
    return guild


# coding=utf-8
from typing import Text
from bs4.element import NamespacedAttribute
import requests
import re
from bs4 import BeautifulSoup
import os
import platform

#更新功能 未来版本预计更新一次性下载所有话的功能,此版本更新可以二次搜索的功能，伪装UA,重写搜索功能,完美兼容Windows,Linux

#全局变量表
header = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 Edg/91.0.864.59'
}#模拟浏览器UA
fake = 'deepdark'#混淆参数？
param = {
    'query':fake#参数
}
manga_name = '' #漫画名称
search_content = '' #搜索页面的html内容
soup = None #soup要分析的
search_results = None #搜索结果
zzbds = re.compile(r'[\s\S.*]+') #要用的正则表达式
aftext = []#处理后的文本集合
mangadoge_search = 'https://dogemanga.com/?q='#搜索前缀
links = []#搜索时找到的链接
texts = []#搜索找到的标题
nextPageButton = None#下一页的按钮
nextPageButton_link = ''#下一页按钮的链接

def manga_search(name):
    search_content = requests.get(url=mangadoge_search+name,params=param,headers=header).text
    print('你现在正在搜索的是:%s'%name)
    soup = BeautifulSoup(search_content,"html.parser")
    search_results = soup.find_all('a',class_="site-red-dot-box site-link")
    manga_count = 0
    for i in search_results:
        links.append(i['href'])
        texts.append(i.get_text())
    if len(texts) >= 13:
        nextPageButton = soup.find_all('a',class_="btn btn-primary btn-lg")
        temp = []
        for i in nextPageButton:
            temp.append(i['href'])
        nextPageButton_link = temp[0]
    for i in range(len(texts)):
        aftext.extend(zzbds.findall(texts[i]))
    print('下面是找到的所有结果!')
    for i in range(len(aftext)):
        print(str(i)+':'+aftext[i])
        manga_count = manga_count + 1
    manga_count = manga_count - 1
    if len(aftext) >= 13:
        print('13:\n下一页 输入0可以返回上级')
    print('::::共找到' + str(manga_count) + '个结果::')
    while True:
        input1 = int(input('请选择序号:'))
        #print('code block 1')
        if input1 == 13 and len(aftext) >=13:
            search_content = requests.get(url=nextPageButton_link,params=param,headers=header).text
            soup = BeautifulSoup(search_content,"html.parser")
            search_results = soup.find_all('a',class_="site-red-dot-box site-link")
            links.clear()
            texts.clear()
            aftext.clear()
            manga_count = 0
            for i in search_results:
                links.append(i['href'])
                texts.append(i.get_text())
            if len(texts) >= 13:
                nextPageButton = soup.find_all('a',class_="btn btn-primary btn-lg")
                temp1 = []
                for i in nextPageButton:
                    temp1.append(i['href'])
                nextPageButton_link = temp1[0]
                #print('code block 2')
            for i in range(len(texts)):
                aftext.extend(zzbds.findall(texts[i]))
            print('以下是找到的所有结果!')
            for i in range(len(aftext)):
                print(str(i) + ':' + aftext[i])
                manga_count = i
            if len(aftext) >= 13:
                print('13:\n下一页 输入0即可返回上一级')
            print('::::共找到' + str(manga_count) + '个结果::')
        elif input1 > 0:
            manga_details(links[input1])
            print('\n\n\n\n\n\n\n\n\n')
            print('下面是找到的所有结果!')
            for i in range(len(aftext)):
                print(str(i)+':'+aftext[i])
                manga_count = i
            manga_count = manga_count - 1
            if len(aftext) >= 13:
                print('13:\n下一页 输入0可以返回上级')
            print('::::共找到' + str(manga_count) + '个结果::')
        elif input1 == 0:
            links.clear()
            texts.clear()
            aftext.clear()
            manga_count = 0
            if platform.system() == "Windows":
                os.system("cls")
            elif platform.system() == "Linux":
                os.system("clear")
            break

def manga_details(lnk):
    print('正在获取漫画详细页面... Now Loading...')
    print('::提示输入1919810可以订阅,输入514810取消订阅::::')
    print(':::manga details page:::')
    dtl_dkmt = requests.get(url=lnk,params=param,headers=header).text #detail document 详细文档
    aftdoc = BeautifulSoup(dtl_dkmt,"html.parser")
    manga_Mtitle = aftdoc.find_all('h2',class_="site-red-dot-box")#漫画 大标题
    name1 = []#不得不说这真的麻烦
    name2 = []
    name3 = ''
    zzbds1 = re.compile(r'[\S.*]+')
    for i in manga_Mtitle:
        name1.append(i.get_text())
    for i in range(len(name1)):
        name2.extend(zzbds1.findall(name1[i]))
    for i in range(len(name2)):
        name3 = name3 + name2[i]
    print('漫画标题:' + name3)#都是为了提取标题
    manga_details_info = aftdoc.find_all('p',class_="lead text-truncate mt-3")#漫画简介
    jianjie = []
    jianjie1 = ''
    for it in manga_details_info:
        jianjie.append(it.get_text())
    for it1 in range(len(jianjie)):
        jianjie1 = jianjie1 + jianjie[it1]
    print('漫画简介:' + jianjie1)#简介
    #获取订阅状态
    if os.path.exists('sub.txt'):
        f = open('sub.txt','r')
        txt1 = f.read()
        f.close()
        if name3 in txt1:
            print('|订阅状态:已订阅|')
        else:
            print('|订阅状态:还没有订阅|')
    else:
        print('|你没有订阅这部本|')
    #订阅结束
    print('--章节------------------')
    hua = aftdoc.find_all('option')
    hua_title = []
    hua_links = []
    total_hon = 0
    for boynextdoor in hua:
        hua_title.append(boynextdoor.get_text())
        hua_links.append(boynextdoor['value'])
    for asswecan in range(len(hua_title)):
        print(str(asswecan) + ':' + str(hua_title[asswecan]))
        total_hon = total_hon + 1
    #print('test::::',hua_links)
    while True:
        print('共有' + str(total_hon) + '个章节')
        input1 = int(input('请输入序号来进行下载,输入114514返回上一级:'))
        if input1 == 114514:
            break
        elif input1 == 1919810:#订阅
            if os.path.exists('sub.txt'):
                fr = open('sub.txt','r')
                if name3 in fr.read():
                    print('你已经订阅了这本本子了哦')
                    fr.close()
                else:
                    fr.close()
                    f = open('sub.txt','r')
                    data = f.read().split('\n')
                    f.close()
                    data.append(name3 + '|' + lnk+';')
                    exp = re.compile(r'\S+')
                    dat = []
                    for i in range(len(data)):
                        dat.extend(exp.findall(data[i]))
                    f1 = open('sub.txt','w')
                    for i in dat:
                        f1.write(i)
                    f1.close()
                    print('已订阅!')
            else:
                f1 = open('sub.txt','w')
                f1.write(name3 + '|' + lnk + ';')
                f1.close()
                print('已订阅!')
        elif input1 == 514810:#取消订阅
            if os.path.exists('sub.txt'):
                flread = open('sub.txt','r')
                if name3 in flread.read():
                    flread.close()
                    read1 = open('sub.txt','r')
                    dat = read1.read()
                    read1.close()
                    fg = dat.split(';')
                    r2 = open('sub.txt','w')
                    mh = None
                    for i in fg:
                        if name3 in i:
                            mh = True
                        if not name3 in i:
                            if mh == True:
                                r2.write(i)
                            else:
                                r2.write(i+';')
                    r2.close()
                    print('已取消订阅!')
                else:
                    flread.close()
                    print('没订阅取消什么呢?')
            else:
                print('没有订阅要怎么取消呢?')
        elif input1 < 114514:
            manga_downloader(hua_links[input1],hua_title[input1],name3)
            if platform.system() == "Windows":
                os.system("cls")
            elif platform.system() == "Linux":
                os.system("clear")
            print('::下载已完成:::')
            print('漫画标题:' + name3)
            print('漫画简介:' + jianjie1)#简介
            print('--章节------------------')
            for i in range(len(hua_title)):
                print(str(i)+ ':' + str(hua_title[i]))

def manga_downloader(link,hua,title):
    print('::::::Download::::::')
    print('-开始下载 ' + title + hua + '-')
    if not os.path.exists("img"):
        os.mkdir("img")
    res = re.compile(r'\S+')
    hua1 = res.findall(hua)
    hua2 = ''
    for i1 in range(len(hua1)):
        hua2 = hua2 + str(hua1[i1])
    if not os.path.exists("img/" + title +"/"):
        os.mkdir("img/" + title +"/" )
    if not os.path.exists("img/" + title +"/"+ str(hua2)):
        os.mkdir("img/" + title +"/"+ str(hua2))
    page = requests.get(link).text
    res1 = re.compile(r'"(https://dogemanga.com/images/pages/.+?.jpg)"')
    reg = re.findall(res1,page)
    num = 0
    for i in reg:
        a = requests.get(i)
        f = open("img/" + title +"/"+ str(hua2)+"/" + "%s.jpg"%num,'wb')
        f.write(a.content)
        f.close()
        print('第' + str(num) +'页',"下载完了...")
        num = num + 1

#主函数
if __name__ == '__main__':
    print('欢迎使用漫画狗自动爬图脚本 图源:dogemanga.com!\nscript by Reiz')
    help = '输入/exit来退出程序\n/settings来设置程序\n/version来查看版本\n/sr 关键字 来进行搜索\n/help 来获取帮助\n/cls来清屏\n/sub来查看订阅\n/mrtj 每日推荐'#帮助文本
    print(help)
    while True:
        command = input('请输入指令:')
        if ('/sr' in command):
            data1 = command.split()
            manga_name = data1[1]
            manga_search(manga_name)
            #print(manga_name)
            #break
        elif command == '/exit':
            break
        elif command == '/settings':
            print('目前仅能设置用来伪装的UA,请你输入你想伪装用的UA')
            input_UA = str(input('请输入:'))
            if input_UA != '':
                header = {
                'User-Agent':input_UA
                }
                print('设置成功!')
        elif command == '/version':
            print('----VERSION---------')
            print('当前版本为 1.2\n更新内容为:新增订阅和每日推荐(只推荐Reiz喜欢的东西\n更新日期为2021/7/4\n最初的1.0版本 制作日期为2021/7/1\n    script by Reiz')
            print('--------------------')
        elif command == '/help':
            print(help)
        elif command == '/cls':
            if platform.system() == "Windows":
                os.system("cls")
            elif platform.system() == "Linux":
                os.system("clear")
        elif command == '/sub':
            print('你订阅了:')
            if os.path.exists('sub.txt'):
                try:
                    sub_file = open('sub.txt','r')
                    print('文件读取成功!\n:::订阅Sub::::::')
                    sub = sub_file.read()
                    sub_file.close()
                    sub_fg = sub.split(';')
                    #print(sub_fg)
                    sub_txt = []
                    aft_sub_titles = []
                    aft_sub_links = []
                    for i in sub_fg:
                        if i != '':
                            sub_txt.append(i)
                    #print(sub_txt)
                    for i in range(len(sub_txt)):
                        temp = sub_txt[i]
                        temp1 = temp.split('|')
                        aft_sub_titles.append(temp1[0])
                        aft_sub_links.append(temp1[1])
                    #print(aft_sub_titles,aft_sub_links)
                    for i in range(len(aft_sub_titles)):
                        print(str(i)+':'+aft_sub_titles[i])
                    while True:
                        inp = int(input('输入你要打开的订阅(114514返回):'))
                        if inp < 0 or inp > 114514:
                            print('超出范围!请重新输入!')
                        elif inp < 114514 and inp >= 0:
                            manga_details(aft_sub_links[inp])
                            print('::订阅Sub:::::')
                            sub_file = open('sub.txt','r')
                            sub = sub_file.read()
                            sub_file.close()
                            sub_fg = sub.split(';')

                            sub_txt.clear()
                            aft_sub_titles.clear()
                            aft_sub_links.clear()
                            for i in sub_fg:
                                if i != '':
                                    sub_txt.append(i)
                            for i in range(len(sub_txt)):
                                temp = sub_txt[i]
                                temp1 = temp.split('|')
                                aft_sub_titles.append(temp1[0])
                                aft_sub_links.append(temp1[1])
                            for i in range(len(aft_sub_titles)):
                                print(str(i)+':'+aft_sub_titles[i])
                        elif inp == 114514:
                            print('返回...')
                            break
                except Exception as e:
                    print('获取订阅失败了!错误如下:')
                    print(e)
            else:
                print('你还没订阅任何东西哟~')
        elif command == '/mrtj':
            print('无法连接到服务器，获取数据失败!')

    print('已退出程序...')