import tensorflow as tf
import numpy as np
from collections import namedtuple
import math
from Model import AlexNet
from Dataset import Dataset
import random
from VGG import vgg_net

# FedModel 定义包含属性 x,y,drop_rate,train_op,loss_op,acc_op等属性
FedModel = namedtuple('FedModel', 'X Y DROP_RATE train_op loss_op acc_op')

#联邦模型客户端类
class Clients:
    def __init__(self, input_shape, num_classes, learning_rate, clients_num):
        self.graph = tf.Graph()
        self.sess = tf.Session(graph=self.graph)

        # 创建alxnet网络
        net = AlexNet(input_shape, num_classes, learning_rate, self.graph)
        #net = vgg_net(input_shape, num_classes, learning_rate, self.graph)
        self.model = FedModel(*net)

        # 初始化
        with self.graph.as_default():
            self.sess.run(tf.global_variables_initializer())

        # 装载数据
        # 根据训练客户端数量划分数据集
        self.dataset = Dataset(tf.keras.datasets.cifar10.load_data,split=clients_num)

        #self.dataset = Dataset(tf.keras.datasets.mnist.load_data,split=clients_num)

    #测试模型准确率
    def run_test(self, num, save=False):
        with self.graph.as_default():
            batch_x, batch_y = self.dataset.test.next_batch(num)
            #替代计算图中的x,y等数据
            feed_dict = {
                self.model.X: batch_x,
                self.model.Y: batch_y,
                self.model.DROP_RATE: 0
            }
        return self.sess.run([self.model.acc_op, self.model.loss_op],
                             feed_dict=feed_dict)

    def train_epoch(self, cid, batch_size=256, dropout_rate=0.7):
        dataset = self.dataset.train[cid]

        with self.graph.as_default():
            for _ in range(math.ceil(dataset.size // batch_size)):
            #for _ in range(1):
                batch_x, batch_y = dataset.next_batch(batch_size)
                batch_x = data_augmentation(batch_x,batch_y) #做数据增强处理

                feed_dict = {
                    self.model.X: batch_x,
                    self.model.Y: batch_y,
                    self.model.DROP_RATE: dropout_rate
                }
                self.sess.run(self.model.train_op, feed_dict=feed_dict)

#返回计算图中所有可训练的变量值
    def get_client_vars(self):
        """ Return all of the variables list """
        with self.graph.as_default():
            client_vars = self.sess.run(tf.trainable_variables())
        return client_vars

    def set_global_vars(self, global_vars):
        with self.graph.as_default():
            all_vars = tf.trainable_variables()#获取所有可训练变量
            for variable, value in zip(all_vars, global_vars):
                variable.load(value, self.sess)#加载server端发送的var到模型上

#随机返回ratio比例的客户端并返回编号
    def choose_clients(self, ratio=1.0):
        client_num = self.get_clients_num()
        choose_num = math.floor(client_num * ratio)
        return np.random.permutation(client_num)[:choose_num]

    def get_clients_num(self):
        #返回客户端的数量
        return len(self.dataset.train)

#数据增强
def _random_crop(batch, crop_shape, padding=None):
    oshape = np.shape(batch[0])
    if padding:
        oshape = (oshape[0] + 2*padding, oshape[1] + 2*padding)
    new_batch = []
    npad = ((padding, padding), (padding, padding), (0, 0))
    for i in range(len(batch)):
        new_batch.append(batch[i])
        if padding:
            new_batch[i] = np.lib.pad(batch[i], pad_width=npad,
                                      mode='constant', constant_values=0)
        nh = random.randint(0, oshape[0] - crop_shape[0])
        nw = random.randint(0, oshape[1] - crop_shape[1])
        new_batch[i] = new_batch[i][nh:nh + crop_shape[0],
                                    nw:nw + crop_shape[1]]
    return new_batch

def _random_flip_leftright(batch,batch_y):
    for i in range(len(batch)):
        '''
        filpped_le_re=tf.image.random_flip_left_right(batch_x[i])   #随机左右翻转
        print(type(filpped_le_re))
        np.concatenate(batch_x,filpped_le_re)
        batch_x.append(filpped_le_re)
        batch_y.append(batch_y[i])
        filpped_up_down=tf.image.random_flip_up_down(batch_x[i])    #随机上下翻转
        batch_x.append(filpped_up_down)
        batch_y.append(batch_y[i])

        # 随机设置图片的对比度
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
        batch_x.append(image)
        batch_y.append(batch_y[i])

        # 随机设置图片的色度
        image2 = tf.image.random_hue(image, max_delta=0.3)
        batch_x.append(image2)
        batch_y.append(batch_y[i])

        adjust=tf.image.random_brightness(filpped_up_down,0.4)
        batch_x.append(adjust)
        batch_y.append(batch_y[i])

        '''
        if bool(random.getrandbits(1)):
            batch[i] = np.fliplr(batch[i])

    return batch

def data_augmentation(batch_x,batch_y):
    batch= _random_flip_leftright(batch_x,batch_y)
    batch = _random_crop(batch, [32, 32], 4)
    return batch


import pathlib

cwd = pathlib.Path(__file__).parent

for child in cwd.iterdir():
    if child.match("day-*"):
        init_file = child / "__init__.py"

        try:
            init_file.touch(exist_ok=False)
        except FileExistsError:
            continue


import cv2
import numpy as np
import math
from numpy.ma.core import filled
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise

### Part 1

def detect_points(img, min_distance, rou, pt_num, patch_size, tau_rou, gamma_rou):
    """
    Patchwise Shi-Tomasi point extraction.

    Hints:
    (1) You may find the function cv2.goodFeaturesToTrack helpful. The initial default parameter setting is given in the notebook.

    Args:
        img: Input RGB image.
        min_distance: Minimum possible Euclidean distance between the returned corners. A parameter of cv2.goodFeaturesToTrack
        rou: Parameter characterizing the minimal accepted quality of image corners. A parameter of cv2.goodFeaturesToTrack
        pt_num: Maximum number of corners to return. A parameter of cv2.goodFeaturesToTrack
        patch_size: Size of each patch. The image is divided into several patches of shape (patch_size, patch_size). There are ((h / patch_size) * (w / patch_size)) patches in total given a image of (h x w)
        tau_rou: If rou falls below this threshold, stops keypoint detection for that patch
        gamma_rou: Decay rou by a factor of gamma_rou to detect more points.
    Returns:
        pts: Detected points of shape (N, 2), where N is the number of detected points. Each point is saved as the order of (height-corrdinate, width-corrdinate)
    """
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    h, w, c = img.shape

    Np = pt_num * 0.9 # The required number of keypoints for each patch. `pt_num` is used as a parameter, while `Np` is used as a stopping criterion.

    # YOUR CODE HERE

    kpts_list = []

    h_tops = np.arange(0, h, patch_size)
    w_lefts = np.arange(0, w, patch_size)

    for h_top in h_tops:
        for w_left in w_lefts:
            h_bottom = min(h_top+patch_size, h)
            w_right = min(w_left+patch_size, w)

            patch = img_gray[h_top:h_bottom, w_left:w_right]

            while True:
                kpts = cv2.goodFeaturesToTrack(patch, pt_num, rou, min_distance).reshape((-1, 2))
                kpts += np.array([[h_top, w_left]])
                rou *= gamma_rou
                if len(kpts) > Np or rou < tau_rou:
                    break

            kpts_list.append(kpts)

    pts = np.concatenate(kpts_list)

    # END

    return pts

def detect_points_whole(img, min_distance, rou, pt_num, tau_rou, gamma_rou):
    """
    Patchwise Shi-Tomasi point extraction.

    Hints:
    (1) You may find the function cv2.goodFeaturesToTrack helpful. The initial default parameter setting is given in the notebook.

    Args:
        img: Input RGB image.
        min_distance: Minimum possible Euclidean distance between the returned corners. A parameter of cv2.goodFeaturesToTrack
        rou: Parameter characterizing the minimal accepted quality of image corners. A parameter of cv2.goodFeaturesToTrack
        pt_num: Maximum number of corners to return. A parameter of cv2.goodFeaturesToTrack
        patch_size: Size of each patch. The image is divided into several patches of shape (patch_size, patch_size). There are ((h / patch_size) * (w / patch_size)) patches in total given a image of (h x w)
        tau_rou: If rou falls below this threshold, stops keypoint detection for that patch
        gamma_rou: Decay rou by a factor of gamma_rou to detect more points.
    Returns:
        pts: Detected points of shape (N, 2), where N is the number of detected points. Each point is saved as the order of (height-corrdinate, width-corrdinate)
    """
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    print(img_gray.shape)
    h, w, c = img.shape

    Np = pt_num * 0.9 # The required number of keypoints for each patch. `pt_num` is used as a parameter, while `Np` is used as a stopping criterion.

    # YOUR CODE HERE
    while True:
        kpts = cv2.goodFeaturesToTrack(img_gray, pt_num, rou, min_distance).reshape((-1, 2))
        rou *= gamma_rou
        if len(kpts) > Np or rou < tau_rou:
            break

    pts = kpts
    print(rou)

    # END

    return pts

def extract_point_features(img, pts, window_patch):
    """
    Extract patch feature for each point.

    The patch feature for a point is defined as the patch extracted with this point as the center.

    Note that the returned pts is a subset of the input pts.
    We discard some of the points as they are close to the boundary of the image and we cannot extract a full patch.

    Please normalize the patch features by subtracting the mean intensity and dividing by its standard deviation.

    Args:
        img: Input RGB image.
        pts: Detected point corners from detect_points().
        window_patch: The window size of patch cropped around the point. The final patch is of size (5 + 1 + 5, 5 + 1 + 5) = (11, 11). The center is the given point.
                      For example, suppose an image is of size (300, 400). The point is located at (50, 60). The window size is 5.
                      Then, we use the cropped patch, i.e., img[50-5:50+5+1, 60-5:60+5+1], as the feature for that point. The patch size is (11, 11), so the dimension is 11x11=121.
    Returns:
        pts: A subset of the input points. We can extract a full patch for each of these points.
        features: Patch features of the points of the shape (N, (window_patch*2 + 1)^2), where N is the number of points
    """

    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    img_gray = img_gray.astype(float)
    h, w, c = img.shape

    # YOUR CODE HERE

    pts_list = []
    features_list = []

    for pt in pts:
        pt = pt.astype(int)
        top = pt[0]-window_patch
        bottom = pt[0]+window_patch+1
        left = pt[1]-window_patch
        right = pt[1]+window_patch+1

        if top < 0 or bottom > h or left < 0 or right > w:
            continue

        pts_list.append(pt)
        features_list.append(img_gray[top:bottom, left:right].reshape(-1))

    pts = np.array(pts_list)
    features = np.array(features_list)

    # Normalize features
    eps = 1e-9 # to prevent division by zero
    features = (features - np.mean(features, axis=1, keepdims=True)) / (np.std(features, axis=1, keepdims=True) + eps)

    # End

    return pts, features

def mean_shift_clustering(features, bandwidth):
    """
    Mean-Shift Clustering.

    There are various ways of implementing mean-shift clustering.
    The provided default bandwidth value may not be optimal to your implementation.
    Please fine-tune the bandwidth so that it can give the best result.

    Args:
        img: Input RGB image.
        bandwidth: If the distance between a point and a clustering mean is below bandwidth, this point probably belongs to this cluster.
    Returns:
        clustering: A dictionary, which contains three keys as follows:
                    1. cluster_centers_: a numpy ndarrary of shape [N_c, 2] for the naive point cloud task and [N_c, 121] for the main task (patch features of the point).
                                         Each row is the center of that cluster.
                    2. labels_:  a numpy nadarray of shape [N,], where N is the number of features.
                                 labels_[i] denotes the label of the i-th feature. The label is between [0, N_c - 1]
                    3. bandwidth: bandwith value
    """
    # YOUR CODE HERE

    # Own implementation

    ## Find cluster centers

    stop_threshold = 1e-3 * bandwidth
    centers = []
    centers_n_neighs = []

    for feat in features:

        # Perform mean shift until convergence
        centroid = feat
        while True:
            distances = np.linalg.norm(features-centroid, axis=1)
            neighbors = np.nonzero(distances < bandwidth)[0]
            old_centroid = centroid
            centroid = np.mean(features[neighbors], axis=0)

            if np.linalg.norm(old_centroid-centroid) < stop_threshold:
                break

        # Keep cluster centers that has neighboring points in it's bandwidth
        if len(neighbors) > 0:
            centers.append(centroid)
            centers_n_neighs.append(len(neighbors))

    ## Remove duplicate centers
    # A duplicate is a center whose distance is less than bandwidth from another center.

    # Sort centers according to number of it's neighbors
    sorted_centers = np.array(centers)[np.argsort(np.array(centers_n_neighs))[::-1]]

    # Remove duplicate
    uniq = np.full((len(centers)), True)
    for i, center in enumerate(sorted_centers):
        if uniq[i]:
            distances = np.linalg.norm(sorted_centers-center, axis=1)
            neighbors = np.nonzero(distances < bandwidth)[0]

            uniq[neighbors] = False
            uniq[i] = True

    cluster_centers = sorted_centers[uniq]

    ## Assign labels
    from scipy.spatial.distance import cdist
    labels = np.argsort(cdist(features, cluster_centers), axis=1)[...,0]

    clustering = {
        "cluster_centers_": cluster_centers,
        "labels_": labels,
        "bandwidth": bandwidth
    }
    # END

    return clustering

def cluster(img, pts, features, bandwidth, tau1, tau2, gamma_h):
    """
    Group points with similar appearance, then refine the groups.

    "gamma_h" provides another way of fine-tuning bandwidth to avoid the number of clusters becoming too large.
    Alternatively, you can ignore "gamma_h" and fine-tune bandwidth by yourself.

    Args:
        img: Input RGB image.
        pts: Output from `extract_point_features`.
        features: Patch feature of points. Output from `extract_point_features`.
        bandwidth: Window size of the mean-shift clustering. In pdf, the bandwidth is represented as "h", but we use "bandwidth" to avoid the confusion with the image height
        tau1: Discard clusters with less than tau1 points
        tau2: Perform further clustering for clusters with more than tau2 points using K-means
        gamma_h: To avoid the number of clusters becoming too large, tune the bandwidth by gradually increasing the bandwidth by a factor gamma_h
    Returns:
        clusters: A list of clusters. Each cluster is a numpy ndarray of shape [N_cp, 2]. N_cp is the number of points of that cluster.
    """
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    img_gray = img_gray.astype(float)
    h, w, c = img.shape

    # YOUR CODE HERE

    from collections import defaultdict

    while True:
        clusters = []

        # Perform mean-shift clustering
        clustering = mean_shift_clustering(features, bandwidth)

        # Gather all clusters' features
        clusters_feats = defaultdict(list)
        clusters_pts = defaultdict(list)
        for i, label in enumerate(clustering["labels_"]):
            clusters_feats[label].append(features[i])
            clusters_pts[label].append(pts[i])

        # Post processing
        for i_c in clusters_feats.keys():
            cluster_feats = clusters_feats[i_c]
            cluster_pts = clusters_pts[i_c]
            n_members = len(cluster_feats)

            # Discard if members less than tau1
            if n_members < tau1:
                pass

            # Keep if members between tau1 and tau2
            elif n_members <= tau2:
                clusters.append(np.array(cluster_pts))

            # Partition when members more than tau2
            else: # len(cluster_pts) > tau2
                kmeans = KMeans(n_clusters=n_members//tau2).fit(cluster_feats)
                kmeans_pts = defaultdict(list)
                for i_k, kmeans_label in enumerate(kmeans.labels_):
                    kmeans_pts[kmeans_label].append(cluster_pts[i_k])

                for kmean_pts in kmeans_pts.values():
                    if len(kmean_pts) >= tau1:
                        clusters.append(np.array(kmean_pts))

        if len(clusters) <= pts.shape[0] / 3:
            break

        bandwidth *= gamma_h

    print(bandwidth)

    # END

    return clusters

### Part 2

def get_proposal(pts_cluster, tau_a, X):
    """
    Get the lattice proposal

    Hints:
    (1) As stated in the lab4.pdf, we give priority to points close to each other when we sample a triplet.
        This statement means that we can start from the three closest points and iterate N_a times.
        There is no need to go through every triplet combination.
        For instance, you can iterate over each point. For each point, you choose 2 of the 10 nearest points. The 3 points form a triplet.
        In this case N_a = num_points * 45.

    (2) It is recommended that you reorder the 3 points.
        Since {a, b, c} are transformed onto {(0, 0), (1, 0), (0, 1)} respectively, the point a is expected to be the vertex opposite the longest side of the triangle formed by these three points

    (3) Another way of refining the choice of triplet is to keep the triplet whose angle (between the edges <a, b> and <a, c>) is within a certain range.
        The range, for instance, is between 20 degrees and 120 degrees.

    (4) You may find `cv2.getAffineTransform` helpful. However, be careful about the HW and WH ordering when you use this function.

    (5) If two triplets yield the same number of inliers, keep the one with closest 3 points.

    Args:
        pts_cluster: Points within the same cluster.
        tau_a: The threshold of the difference between the transformed corrdinate and integer positions.
               For example, if a point is transformed into (1.1, -2.03), the closest integer position is (1, -2), then the distance is sqrt(0.1^2 + 0.03^2) (for Euclidean distance case).
               If it is smaller than "tau_a", we consider this point as inlier.
        X: When we compute the inliers, we only consider X nearest points to the point "a".
    Returns:
        proposal: A list of inliers. The first 3 inliers are {a, b, c}.
                  Each inlier is a dictionary, with key of "pt_int" and "pt" representing the integer positions after affine transformation and orignal coordinates.
    """
    # YOU CODE HERE

    def to_homo(arr):
        return np.concatenate((arr, np.ones((arr.shape[0], 1))), axis=1)

    best_n_inlier = 0

    for pt1 in pts_cluster:
        closest_10 = pts_cluster[np.argsort(np.linalg.norm(pts_cluster-pt1, axis=1))][1:min(11, len(pts_cluster))]
        for i in range(min(10, len(pts_cluster)-1)):
            for j in range(i+1, min(10, len(pts_cluster)-1)):
                pt2 = closest_10[i]
                pt3 = closest_10[j]

                # Reorder to choose a, b, c
                sides = np.linalg.norm([pt2-pt3, pt1-pt3, pt1-pt2], axis=1)

                a, b, c = np.array([pt1, pt2, pt3])[np.argsort(sides)[::-1]]

                # Discard extreme angle
                ab_vector = (a-b) / np.linalg.norm(a-b)
                ac_vector = (a-c) / np.linalg.norm(a-c)
                angle = np.rad2deg(np.arccos(np.clip(np.dot(ab_vector, ac_vector), -1.0, 1.0)))
                if angle < 20 or angle > 120:
                    continue

                # Compute affine transform
                src = np.array([[a[1], a[0]], [b[1], b[0]], [c[1], c[0]]], dtype=np.float32)
                dst = np.array([[0, 0], [1, 0], [0, 1]], dtype=np.float32)
                M = cv2.getAffineTransform(src, dst)

                # Count inliers
                pts_cluster_without_abc = np.array([pt for pt in pts_cluster if not(np.array_equal(pt, a) or np.array_equal(pt, b) or np.array_equal(pt, c))])
                closest_X = pts_cluster_without_abc[np.argsort(np.linalg.norm(pts_cluster_without_abc-a, axis=1))][:X-3]
                transformed = (M @ to_homo(np.fliplr(closest_X)).T).T
                rounded = np.rint(transformed).astype(int)
                distance_to_rounded = np.linalg.norm(transformed-rounded, axis=1)
                inlier_mask = distance_to_rounded < tau_a

                inliers = closest_X[inlier_mask]
                inliers_rounded = rounded[inlier_mask]

                if inliers.shape[0] >= best_n_inlier:
                    if inliers.shape[0] == best_n_inlier and best_n_inlier > 0:
                        best_inliers_dist = np.linalg.norm(best_inliers[0]-best_inliers[1]) + np.linalg.norm(best_inliers[0]-best_inliers[2])
                        cur_inliers_dist = np.linalg.norm(a-b) + np.linalg.norm(a-c)
                        if cur_inliers_dist >= best_inliers_dist:
                            continue
                    best_n_inlier = inliers.shape[0] + 3
                    best_inliers = np.concatenate(([a, b, c], inliers))
                    best_inliers_rounded = np.concatenate(([[0, 0], [1, 0], [0, 1]], inliers_rounded))

    proposal = []
    for i in range(best_n_inlier):
        proposal.append({"pt_int": best_inliers_rounded[i], "pt": best_inliers[i]})

    # END

    return proposal

def find_texels(img, proposal, texel_size=50):
    """
    Find texels from the given image.

    Hints:
    (1) This function works on RGB image, unlike previous functions such as point detection and clustering that operate on grayscale image.

    (2) You may find `cv2.getPerspectiveTransform` and `cv2.warpPerspective` helpful.
        Please refer to the demo in the notebook for the usage of the 2 functions.
        Be careful about the HW and WH ordering when you use this function.

    (3) As stated in the pdf, each texel is defined by 3 or 4 inlier keypoints on the corners.
        If you find this sentence difficult to understand, you can go to check the demo.
        In the demo, a corresponding texel is obtained from 3 points. The 4th point is predicted from the 3 points.

    Args:
        img: Input RGB image
        proposal: Outputs from get_proposal(). Proposal is a list of inliers.
        texel_size: The patch size (U, V) of the patch transformed from the quadrilateral.
                    In this implementation, U is equal to V. (U = V = texel_size = 50.) The texel is a square.
    Returns:
        texels: A numpy ndarray of the shape (#texels, texel_size, texel_size, #channels).
    """
    # YOUR CODE HERE

    from itertools import combinations

    texels = []

    visited_quads = set()
    visited_tris = set()

    pts_dict = {tuple(inlier['pt_int']): i for i, inlier in enumerate(proposal)}

    for combo in combinations(range(len(proposal)), 3):

        pt1 = proposal[combo[0]]['pt_int']
        pt2 = proposal[combo[1]]['pt_int']
        pt3 = proposal[combo[2]]['pt_int']

        # Skip if triplets has been visited before
        if tuple(sorted([tuple(pt1.tolist()), tuple(pt2.tolist()), tuple(pt3.tolist())])) in visited_tris:
            continue
        else:
            visited_tris.add(tuple(sorted([tuple(pt1.tolist()), tuple(pt2.tolist()), tuple(pt3.tolist())])))

        sides = np.linalg.norm(np.array([
            pt2-pt3,
            pt1-pt3,
            pt1-pt2
            ]), axis=1)

        ideal_sides = np.linalg.norm(np.array([
            [1,0],
            [0,1],
            [1,-1]
            ]), axis=1)

        # Verify connected-ness
        if sorted(sides.tolist()) != sorted(ideal_sides.tolist()):
            continue

        # Infer 4th point
        i_a, i_b, i_c = np.array([combo[0], combo[1], combo[2]])[np.argsort(sides)[::-1]]
        a_int, b_int, c_int = (proposal[i]['pt_int'] for i in [i_a, i_b, i_c])
        a, b, c = (proposal[i]['pt'] for i in [i_a, i_b, i_c])
        d_int = a_int + (b_int - a_int) + (c_int - a_int)
        d = a + (b - a) + (c - a)

        # Check whether 4th point is also in proposal
        if tuple(d_int) in pts_dict:
            i_d = pts_dict[tuple(d_int)]
            # Skip if quadruplets has been visited before
            if tuple(sorted([i_a, i_b, i_c, i_d])) in visited_quads:
                continue
            else:
                visited_quads.add(tuple(sorted([i_a, i_b, i_c, i_d])))

        # Reorder the points as ([0, 0], [0, 1], [1, 0], [1, 1])
        order = np.argsort(np.array([tuple(a_int), tuple(b_int), tuple(c_int), tuple(d_int)], dtype="i,i"))
        a, b, c, d = np.array([tuple(a), tuple(b), tuple(c), tuple(d)], dtype="i,i")[order].tolist()

        # Warp texel
        src = np.fliplr(np.array([a, b, c, d], dtype=np.float32))
        dst = np.array([[0, 0], [0, texel_size], [texel_size, 0], [texel_size, texel_size]], dtype=np.float32)
        M = cv2.getPerspectiveTransform(src, dst)
        texel = cv2.warpPerspective(img, M, (texel_size, texel_size))

        texels.append(texel)

    texels = np.array(texels)

    # END
    return texels

def score_proposal(texels, a_score_count_min=3):
    """
    Calcualte A-Score.

    Hints:
    (1) Each channel is normalized separately.
        The A-score for a RGB texel is the average of 3 A-scores of each channel.

    (2) You can return 1000 (in our example) to denote a invalid A-score.
        An invalid A-score is usually results from clusters with less than "a_score_count_min" texels.

    Args:
        texels: A numpy ndarray of the shape (#texels, window, window, #channels).
        a_score_count_min: Minimal number of texels we need to calculate the A-score.
    Returns:
        a_score: A-score calculated from the texels. If there are no sufficient texels, return 1000.
    """

    if texels.shape[0] == 0:
        return 1000

    K, U, V, C = texels.shape

    # YOUR CODE HERE

    # Normalize texels
    eps = 1e-9 # to prevent division by zero
    texels = (texels - np.mean(texels, axis=(1,2), keepdims=True)) / (np.std(texels, axis=(1,2), keepdims=True) + eps)

    if K < a_score_count_min:
        a_score = 1000
    else:
        a_score = np.mean(np.sum(np.std(texels, axis=0), axis=(0,1), keepdims=False) / (U*V*K))

    # END

    return a_score

### Part 3
# You are free to change the input argument of the functions in Part 3.
# GIVEN
def non_max_suppression(response, suppress_range, threshold=None):
    """
    Non-maximum Suppression for translation symmetry detection

    The general approach for non-maximum suppression is as follows:
        1. Perform thresholding on the input response map. Set the points whose values are less than the threshold as 0.
        2. Find the largest response value in the current response map
        3. Set all points in a certain range around this largest point to 0.
        4. Save the current largest point
        5. Repeat the step from 2 to 4 until all points are set as 0.
        6. Return the saved points are the local maximum.

    Args:
        response: numpy.ndarray, output from the normalized cross correlation
        suppress_range: a tuple of two ints (H_range, W_range). The points around the local maximum point within this range are set as 0. In this case, there are 2*H_range*2*W_range points including the local maxima are set to 0
    Returns:
        threshold: int, points with value less than the threshold are set to 0
    """
    H, W = response.shape[:2]
    H_range, W_range = suppress_range
    res = np.copy(response)

    if threshold is not None:
        res[res<threshold] = 0

    idx_max = res.reshape(-1).argmax()
    x, y = idx_max // W, idx_max % W
    point_set = set()
    while res[x, y] != 0:
        point_set.add((x, y))
        res[max(x - H_range, 0): min(x+H_range, H), max(y - W_range, 0):min(y+W_range, W)] = 0
        idx_max = res.reshape(-1).argmax()
        x, y = idx_max // W, idx_max % W
    for x, y in point_set:
        res[x, y] = response[x, y]
    return res

def template_match(img, proposal, threshold):
    """
    Perform template matching on the original input image.

    Hints:
    (1) You may find cv2.copyMakeBorder and cv2.matchTemplate helpful. The cv2.copyMakeBorder is used for padding.
        Alternatively, you can use your implementation in Lab 1 for template matching.

    (2) For non-maximum suppression, you can either use the one you implemented for lab 1 or the code given above.

    Returns:
        response: A sparse response map from non-maximum suppression.
    """
    # YOUR CODE HERE

    a, b, c = (proposal[i]['pt'] for i in range(3))
    d = a + (b - a) + (c - a)

    top = min(a[0], b[0], c[0], d[0])
    bottom = max(a[0], b[0], c[0], d[0]) + 1
    left = min(a[1], b[1], c[1], d[1])
    right = max(a[1], b[1], c[1], d[1]) + 1

    template = img[top:bottom, left:right]

    pad_top, pad_bottom = template.shape[0] // 2 - (1 if template.shape[0] % 2 == 0 else 0), template.shape[0] // 2
    pad_left, pad_right = template.shape[1] // 2 - (1 if template.shape[1] % 2 == 0 else 0), template.shape[1] // 2
    img_padded = cv2.copyMakeBorder(img, pad_top, pad_bottom, pad_left, pad_right, cv2.BORDER_CONSTANT, None, [0,0,0])

    res = cv2.matchTemplate(img_padded, template, cv2.TM_CCOEFF_NORMED)

    response = non_max_suppression(res, (int(template.shape[0] * 0.8), int(template.shape[1] * 0.8)), threshold=threshold)

    # END
    return response

def maxima2grid(img, proposal, response):
    """
    Estimate 4 lattice points from each local maxima.

    Hints:
    (1) We can transfer the 4 offsets between the center of the original template and 4 lattice unit points to new detected centers.

    Args:
        response: The response map from `template_match()`.

    Returns:
        points_grid: an numpy ndarray of shape (N, 2), where N is the number of grid points.

    """
    # YOUR CODE HERE

    a, b, c = (proposal[i]['pt'] for i in range(3))
    d = a + (b - a) + (c - a)

    corners = np.array([a, b, c, d])

    top, left = np.min(corners, axis=0)
    bottom, right = np.max(corners, axis=0)

    center = np.array([(top+bottom)//2, (left+right)//2])

    offsets = corners - center

    points_grid_list = []

    for max_center in np.argwhere(response):
        points_grid_list.append(offsets + max_center)

    points_grid = np.concatenate(points_grid_list)

    # END

    return points_grid

# def refine_grid(img, proposal, points_grid, merge_dist=40, quantile_thresh=0.01):
    """
    Refine the detected grid points.

    Args:
        points_grid: The output from the `maxima2grid()`.

    Returns:
        points: A numpy ndarray of shape (N, 2), where N is the number of refined grid points.
    """
    # YOUR CODE HERE
    from scipy.spatial.distance import cdist

    def merge_points(points_grid):
        # Merge close points
        merged_points = []
        # merge_dist = 20

        pairwise_dist = cdist(points_grid, points_grid)
        closest_neigh = np.argsort(pairwise_dist, axis=1)[:,1:]

        visited_j = set()
        for i, js in enumerate(closest_neigh):
            if i in visited_j:
                continue
            for j in js:
                if pairwise_dist[i,j] < merge_dist:
                    visited_j.add(j)
                    continue

                if j == js[0]:
                    merged_points.append(points_grid[i])
                else:
                    merged_points.append((points_grid[i] + points_grid[js[0]])//2)
                break

        return np.array(merged_points)

    def fill_points(merged_points):

        # quantile_thresh = 0.01

        # Fill in missing points
        pairwise_diff = merged_points[np.newaxis, :, :] - merged_points[:, np.newaxis, :]
        pairwise_angle = np.rad2deg(np.arctan2(pairwise_diff[...,0], (pairwise_diff[...,1])))

        # top
        top_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 60, pairwise_angle < 120))]
        top_right_dist = np.linalg.norm(top_right_diff, axis=-1)
        nonzero_mask = top_right_dist > 0
        top_right_diff = top_right_diff[nonzero_mask]
        top_right_dist = top_right_dist[nonzero_mask]
        top_right_off = np.mean(top_right_diff[top_right_dist < np.quantile(top_right_dist, quantile_thresh)], axis=0)

        # left
        top_left_diff = pairwise_diff[np.nonzero(np.logical_or(pairwise_angle > 150, pairwise_angle < -150))]
        top_left_dist = np.linalg.norm(top_left_diff, axis=-1)
        nonzero_mask = top_left_dist > 0
        top_left_diff = top_left_diff[nonzero_mask]
        top_left_dist = top_left_dist[nonzero_mask]
        top_left_off = np.mean(top_left_diff[top_left_dist < np.quantile(top_left_dist, quantile_thresh)], axis=0)

        # bottom
        bottom_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > -120, pairwise_angle < -60))]
        bottom_left_dist = np.linalg.norm(bottom_left_diff, axis=-1)
        nonzero_mask = bottom_left_dist > 0
        bottom_left_diff = bottom_left_diff[nonzero_mask]
        bottom_left_dist = bottom_left_dist[nonzero_mask]
        bottom_left_off = np.mean(bottom_left_diff[bottom_left_dist < np.quantile(bottom_left_dist, quantile_thresh)], axis=0)

        # right
        bottom_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < 30, pairwise_angle > -30))]
        bottom_right_dist = np.linalg.norm(bottom_right_diff, axis=-1)
        nonzero_mask = bottom_right_dist > 0
        bottom_right_diff = bottom_right_diff[nonzero_mask]
        bottom_right_dist = bottom_right_dist[nonzero_mask]
        bottom_right_off = np.mean(bottom_right_diff[bottom_right_dist < np.quantile(bottom_right_dist, quantile_thresh)], axis=0)

        filled_points = []
        for pt in merged_points:
            filled_points.extend([pt+top_right_off, pt+top_left_off, pt+bottom_left_off, pt+bottom_right_off])

        filled_points.extend(merged_points)

        return np.array(filled_points)

    merged_points = merge_points(points_grid)
    filled_points = fill_points(merged_points)
    points = np.array(merge_points(filled_points))

    # END

    return points

def refine_grid(img, proposal, points_grid):
    """
    Refine the detected grid points.

    Args:
        points_grid: The output from the `maxima2grid()`.

    Returns:
        points: A numpy ndarray of shape (N, 2), where N is the number of refined grid points.
    """
    # YOUR CODE HERE
    from scipy.spatial.distance import cdist

    def merge_points(points_grid):
        # Merge close points
        merged_points = []
        merge_dist = 20

        pairwise_dist = cdist(points_grid, points_grid)
        closest_neigh = np.argsort(pairwise_dist, axis=1)[:,1:]

        visited_j = set()
        for i, js in enumerate(closest_neigh):
            if i in visited_j:
                continue
            for j in js:
                if pairwise_dist[i,j] < merge_dist:
                    visited_j.add(j)
                    continue

                if j == js[0]:
                    merged_points.append(points_grid[i])
                else:
                    merged_points.append((points_grid[i] + points_grid[js[0]])//2)
                break

        return np.array(merged_points)

    def fill_points(merged_points):

        quantile_thresh = 0.01

        # Fill in missing points
        pairwise_diff = merged_points[np.newaxis, :, :] - merged_points[:, np.newaxis, :]
        pairwise_angle = np.rad2deg(np.arctan2(pairwise_diff[...,0], (pairwise_diff[...,1])))

        # top-right
        top_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 30, pairwise_angle < 60))]
        top_right_dist = np.linalg.norm(top_right_diff, axis=-1)
        nonzero_mask = top_right_dist > 0
        top_right_diff = top_right_diff[nonzero_mask]
        top_right_dist = top_right_dist[nonzero_mask]
        top_right_off = np.mean(top_right_diff[top_right_dist < np.quantile(top_right_dist, quantile_thresh)], axis=0)

        # print(top_right_off)

        # top-left
        top_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 120, pairwise_angle < 150))]
        top_left_dist = np.linalg.norm(top_left_diff, axis=-1)
        nonzero_mask = top_left_dist > 0
        top_left_diff = top_left_diff[nonzero_mask]
        top_left_dist = top_left_dist[nonzero_mask]
        top_left_off = np.mean(top_left_diff[top_left_dist < np.quantile(top_left_dist, quantile_thresh)], axis=0)

        # print(top_left_off)

        # bottom-left
        bottom_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < -120, pairwise_angle > -150))]
        bottom_left_dist = np.linalg.norm(bottom_left_diff, axis=-1)
        nonzero_mask = bottom_left_dist > 0
        bottom_left_diff = bottom_left_diff[nonzero_mask]
        bottom_left_dist = bottom_left_dist[nonzero_mask]
        bottom_left_off = np.mean(bottom_left_diff[bottom_left_dist < np.quantile(bottom_left_dist, quantile_thresh)], axis=0)

        # print(bottom_left_off)

        # bottom-right
        bottom_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < -30, pairwise_angle > -60))]
        bottom_right_dist = np.linalg.norm(bottom_right_diff, axis=-1)
        nonzero_mask = bottom_right_dist > 0
        bottom_right_diff = bottom_right_diff[nonzero_mask]
        bottom_right_dist = bottom_right_dist[nonzero_mask]
        bottom_right_off = np.mean(bottom_right_diff[bottom_right_dist < np.quantile(bottom_right_dist, quantile_thresh)], axis=0)

        # print(bottom_right_off)

        filled_points = []
        for pt in merged_points:
            filled_points.extend([pt+top_right_off, pt+top_left_off, pt+bottom_left_off, pt+bottom_right_off])

        filled_points.extend(merged_points)

        return np.array(filled_points)

    merged_points = merge_points(points_grid)
    filled_points = fill_points(merged_points)
    points = np.array(merge_points(filled_points))

    # END

    return points

# def grid2latticeunit(img, proposal, points):
    """
    Convert each lattice grid point into integer lattice grid.

    Hints:
    (1) Since it is difficult to know whether two points should be connected, one way is to map each point into an integer position.
        The integer position should maintain the spatial relationship of these points.
        For instance, if we have three points x1=(50, 50), x2=(70, 50) and x3=(70, 70), we can map them (4, 5), (5, 5) and (5, 6).
        As the distances between (4, 5) and (5, 5), (5, 5) and (5, 6) are both 1, we know that (x1, x2) and (x2, x3) form two edges.

    (2) You can use affine transformation to build the mapping above, but do not perform global affine transformation.

    (3) The mapping in the hints above are merely to know whether two points should be connected.
        If you have your own method for finding the relationship, feel free to implement your owns and ignore the hints above.

    Returns:
        edges: A list of edges in the lattice structure. Each edge is defined by two points. The point coordinate is in the image coordinate.
    """

    # YOUR CODE HERE

    from scipy.spatial.distance import cdist

    def find_avg_offset(points, quantile_thresh=0.01):
        # Find average offset between points

        pairwise_diff = points[np.newaxis, :, :] - points[:, np.newaxis, :]
        pairwise_angle = np.rad2deg(np.arctan2(pairwise_diff[...,0], (pairwise_diff[...,1])))

        # top
        top_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 60, pairwise_angle < 120))]
        top_right_dist = np.linalg.norm(top_right_diff, axis=-1)
        nonzero_mask = top_right_dist > 0
        top_right_dist = top_right_dist[nonzero_mask]
        top_right = np.mean(top_right_dist[top_right_dist < np.quantile(top_right_dist, quantile_thresh)], axis=0)

        # left
        top_left_diff = pairwise_diff[np.nonzero(np.logical_or(pairwise_angle > 150, pairwise_angle < -150))]
        top_left_dist = np.linalg.norm(top_left_diff, axis=-1)
        nonzero_mask = top_left_dist > 0
        top_left_dist = top_left_dist[nonzero_mask]
        top_left = np.mean(top_left_dist[top_left_dist < np.quantile(top_left_dist, quantile_thresh)], axis=0)

        # bottom
        bottom_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > -120, pairwise_angle < -60))]
        bottom_left_dist = np.linalg.norm(bottom_left_diff, axis=-1)
        nonzero_mask = bottom_left_dist > 0
        bottom_left_dist = bottom_left_dist[nonzero_mask]
        bottom_left = np.mean(bottom_left_dist[bottom_left_dist < np.quantile(bottom_left_dist, quantile_thresh)], axis=0)

        # right
        bottom_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < 30, pairwise_angle > -30))]
        bottom_right_dist = np.linalg.norm(bottom_right_diff, axis=-1)
        nonzero_mask = bottom_right_dist > 0
        bottom_right_dist = bottom_right_dist[nonzero_mask]
        bottom_right = np.mean(bottom_right_dist[bottom_right_dist < np.quantile(bottom_right_dist, quantile_thresh)], axis=0)

        return [top_right, top_left, bottom_left, bottom_right]

    offsets = find_avg_offset(points)

    pairwise_dist = cdist(points, points)

    connected_tol = 0.22

    edges = []
    for i in range(len(points)):
        for j in range(i+1, len(points)):
            diff = points[i] - points[j]
            angle = np.rad2deg(np.arctan2(diff[0], diff[1]))
            if 45 <= angle < 135:
                 offset = offsets[0]
            elif 135 <= angle or angle < -135:
                offset = offsets[1]
            elif -135 <= angle < 45:
                offset = offsets[2]
            else:
                offset = offsets[3]
            if (1-connected_tol)*offset < pairwise_dist[i,j] < (1+connected_tol)*offset:
                edges.append([points[i].astype(int).tolist(), points[j].astype(int).tolist()])

    # END

    return edges

def grid2latticeunit(img, proposal, points):
    """
    Convert each lattice grid point into integer lattice grid.

    Hints:
    (1) Since it is difficult to know whether two points should be connected, one way is to map each point into an integer position.
        The integer position should maintain the spatial relationship of these points.
        For instance, if we have three points x1=(50, 50), x2=(70, 50) and x3=(70, 70), we can map them (4, 5), (5, 5) and (5, 6).
        As the distances between (4, 5) and (5, 5), (5, 5) and (5, 6) are both 1, we know that (x1, x2) and (x2, x3) form two edges.

    (2) You can use affine transformation to build the mapping above, but do not perform global affine transformation.

    (3) The mapping in the hints above are merely to know whether two points should be connected.
        If you have your own method for finding the relationship, feel free to implement your owns and ignore the hints above.

    Returns:
        edges: A list of edges in the lattice structure. Each edge is defined by two points. The point coordinate is in the image coordinate.
    """

    # YOUR CODE HERE

    from scipy.spatial.distance import cdist

    def find_avg_offset(points, quantile_thresh=0.01):
        # Find average offset between points

        pairwise_diff = points[np.newaxis, :, :] - points[:, np.newaxis, :]
        pairwise_angle = np.rad2deg(np.arctan2(pairwise_diff[...,0], (pairwise_diff[...,1])))

        # top-right
        top_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 30, pairwise_angle < 60))]
        top_right_dist = np.linalg.norm(top_right_diff, axis=-1)
        nonzero_mask = top_right_dist > 0
        top_right_dist = top_right_dist[nonzero_mask]
        top_right = np.mean(top_right_dist[top_right_dist < np.quantile(top_right_dist, quantile_thresh)], axis=0)

        # top-left
        top_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle > 120, pairwise_angle < 150))]
        top_left_dist = np.linalg.norm(top_left_diff, axis=-1)
        nonzero_mask = top_left_dist > 0
        top_left_dist = top_left_dist[nonzero_mask]
        top_left = np.mean(top_left_dist[top_left_dist < np.quantile(top_left_dist, quantile_thresh)], axis=0)

        # bottom-left
        bottom_left_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < -120, pairwise_angle > -150))]
        bottom_left_dist = np.linalg.norm(bottom_left_diff, axis=-1)
        nonzero_mask = bottom_left_dist > 0
        bottom_left_dist = bottom_left_dist[nonzero_mask]
        bottom_left = np.mean(bottom_left_dist[bottom_left_dist < np.quantile(bottom_left_dist, quantile_thresh)], axis=0)

        # bottom-right
        bottom_right_diff = pairwise_diff[np.nonzero(np.logical_and(pairwise_angle < -30, pairwise_angle > -60))]
        bottom_right_dist = np.linalg.norm(bottom_right_diff, axis=-1)
        nonzero_mask = bottom_right_dist > 0
        bottom_right_dist = bottom_right_dist[nonzero_mask]
        bottom_right = np.mean(bottom_right_dist[bottom_right_dist < np.quantile(bottom_right_dist, quantile_thresh)], axis=0)

        return np.mean([top_right, top_left, bottom_left, bottom_right])

    avg_offset = find_avg_offset(points)

    pairwise_dist = cdist(points, points)

    connected_tol = 0.3

    edges = []
    for i in range(len(points)):
        for j in range(i+1, len(points)):
            if (1-connected_tol)*avg_offset < pairwise_dist[i,j] < (1+connected_tol)*avg_offset:
                edges.append([points[i].astype(int).tolist(), points[j].astype(int).tolist()])

    # END

    return edges


# -*- coding: utf-8 -*-
"""
Created on Fri Aug 16 00:50:08 2013

@author: akels
"""
class Person:
	def __init__(self, name):
		self._name = name
		print(self.name.setter)

	@property
	def name(self):
		"name property docs"
		print('fetch...')
		return self._name

	@name.setter
	def name(self, value):
		print('change...')
		self._name = value

	@name.deleter
	def name(self):
		print('remove...')
		del self._name

bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name
print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
print(Person.name.__doc__)
# name = property(name)
# name = name.setter(name)
# name = name.deleter(name)
# bob has a managed attribute
# Runs name getter (name 1)
# Runs name setter (name 2)
# Runs name deleter (name 3)
# sue inherits property too
# Or help(Person.name)


# python basic01.py
import math
print(math.sqrt(2)) # 平方根の記述
print(math.pi) # 円周率
val01 = (math.sqrt(5) + 1) / 2 # 5の平方根に1を加えて2で割る(黄金比)
print(val01)

# fフィートiインチをセンチメートルに変換する(1フィート = 12インチ = 30.48cm)
def ft_to_cm(f, i):
  return (f + i / 12) * 30.48

feet = 3
inch = 3
print(f"{feet}フィート + {inch}インチ = {ft_to_cm(feet, inch)}cm")

# mメートルをフィート・インチに変換する
def m_to_ft(m):
  inches =  m * 39.37
  feet =  m * 3.28
  return inches, feet

m = 18.44
inches, feet = m_to_ft(m)
print(f"{m}メートル = {inches}インチ = {feet}フィート")

# 二次関数の値を求める
def quadratic(a, b, c, x):
  return a * x ** 2 + b * x + c

answer = quadratic(1, -5, -2, 7)
print(answer)

# ヘロンの公式
def heron(a, b, c):
  s = (a + b + c) / 2
  return math.sqrt(s * (s - a) * (s - b) * (s - c))

side1 = 3
side2 = 4
side3 = 5
print(f"3辺が{side1}, {side2}, {side3}の三角形の面積:{heron(side1, side2, side3)}")

# 二次方程式の判別式と解qe_disc, qe_solution
def qe_disc(a, b, c):
  return b ** 2 - 4 * a * c

def qe_solution(a, b, c):
  desc1 = (-b + math.sqrt(qe_disc(a, b, c))) / (2 * a)
  desc2 = (-b - math.sqrt(qe_disc(a, b, c))) / (2 * a)
  return desc1, desc2

valueA = 1
valueB = -2
valueC = 1
discriminate = qe_disc(valueA, valueB, valueC) # 判別結果
desc1, desc2 = qe_solution(valueA, valueB, valueC)
print(f"判別結果:{discriminate}")
print(f"解:{desc1}, {desc2}")

# 再帰(0, 1, …)
def fin(n):
  if n < 2:
    return n
  else:
    return fin(n - 2) + fin(n - 1)

print(fin(4))


import importlib
import inspect
import sys

""" Referred from MMS """

def list_model_services(module, parent_class=None):
    """
    Parse user defined module to get all model service classes in it.

    :param module:
    :param parent_class:
    :return: List of model service class definitions
    """

    # Parsing the module to get all defined classes
    classes = [cls[1] for cls in inspect.getmembers(module, lambda member: inspect.isclass(member) and
                                                                           member.__module__ == module.__name__)]
    # filter classes that is subclass of parent_class
    if parent_class is not None:
        return [c for c in classes if issubclass(c, parent_class)]

    return classes

def get_class_name(file_name, function_name):
    if not file_name.endswith(".py"):
        raise ValueError("It has to be a python file")

    module = file_name[:-3]
    module = module.split("/")[-1]
    sys.path.append(file_name[:-(len(module) + 3)])
    module = importlib.import_module(module)

    if module is None:
        raise ValueError("Unable to load module {}, make sure it is added to python path".format(module))

    if hasattr(module, function_name):
        return module
    else:
        model_class_definitions = list_model_services(module)
        if len(model_class_definitions) != 1:
            raise ValueError("Expected only one class in custom service code or a function entry point {}".format(
                model_class_definitions))

        model_class = model_class_definitions[0]
        return model_class


import requests
import json

class Yanerrors(Exception):
    """Base yantran errors class"""
    pass

class InvalidKey(Yanerrors):
    """Your API Key is invalid

    Attributes:
        message -- Yandex refused to accept the provided API Key
    """

    def __init__(self, message):
        self.message = message

class BlockedKey(Yanerrors):
    """Your API Key has been blocked

        Attributes:
            message -- Yandex has blocked the provided API Key
        """

    def __init__(self, message):
        self.message = message

class Yantranslator(object):

    def __init__(self,api_key):
        self.api_key = api_key

    def supported_langs(self,ui_lang='en'):
        url = 'https://translate.yandex.net/api/v1.5/tr.json/getLangs?key='
        user_lang = ui_lang
        r = requests.get('{}{}&'.format(url,self.api_key,user_lang))
        if r.status_code == 401:
            raise InvalidKey
        if r.status_code == 402:
            raise BlockedKey
        data = json.loads(r.text)
        lang_pairs = data['dirs'][0:]
        return lang_pairs

    def indentify_language(self,text,hint=''):
        url = 'https://translate.yandex.net/api/v1.5/tr.json/detect?key='
        text = '&text={}'.format(text)
        if hint == '':
            r = requests.get('{}{}{}'.format(url,self.api_key,text))
            if r.status_code == 401:
                raise InvalidKey
            if r.status_code == 402:
                raise BlockedKey
            data = json.loads(r.text)
            language = data['lang'][0:]
            return language
        else:
            hint = '&hint={}'.format(hint)
            r = requests.get('{}{}{}{}'.format(url,self.api_key,text,hint))
            if r.status_code == 401:
                raise InvalidKey
            if r.status_code == 402:
                raise BlockedKey
            data = json.loads(r.text)
            language = data['lang'][0:]
            return language

    def tran_sentence(self,from_lang,to_lang,sentence,formatting='plain'):
        language_pairing = '&lang={}-{}'.format(from_lang,to_lang)
        url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key='
        text = '&text={}'.format(sentence)
        formatting = '&format={}'.format(formatting)
        r = requests.get('{}{}{}{}{}'.format(url,self.api_key,language_pairing,text,formatting))
        if r.status_code == 401:
            raise InvalidKey
        if r.status_code == 402:
            raise BlockedKey
        data = json.loads(r.text)
        result = data['text'][0:]
        result = ''.join(result)
        return result

    def auto_tran(self,to_lang,sentence,formatting='plain'):
        url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key='
        language = '&lang={}'.format(to_lang)
        options = '&options=1'
        formatting = '&format={}'.format(formatting)
        text = '&text={}'.format(sentence)
        r = requests.get('{}{}{}{}{}{}'.format(url,self.api_key,text,language,formatting,options))
        if r.status_code == 401:
            raise InvalidKey
        if r.status_code == 402:
            raise BlockedKey
        data = json.loads(r.text)
        result = data['text'][0:]
        result = ''.join(result)
        return result


# -*- coding: utf-8 -*-
"""
Created on Thu Jul 29 16:47:03 2021

@author: 潘登
"""

#%%sigmoid 函数
import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    return 1/(1+np.exp(-1*x))

x = np.arange(-10,10,0.01)
y = sigmoid(x)

plt.plot(x,y)
plt.show()

#%%逻辑回归loss图像
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import scale
data = load_breast_cancer()
x,y = scale(data['data'][:,:2]),data['target']   #这里为了方便画图，所以只取x的两列
#求出两个维度对应的数据在逻辑回归算法下的最优解
lr = LogisticRegression(fit_intercept=False)  #为了方便画图，不加截距项
lr.fit(x,y)
#把参数取出来
w = lr.coef_

#已知w的情况下，传进来数据x，返回数据的y_predict
def p_theta_function(feature,w):
    Z  = feature.dot(w.T)     #Z = xθ
    return 1/(1+np.exp(-1*Z))

def loss_fuction(samples_features,samples_labels,w):
    result = 0
    #遍历数据集中的每一条数据，并且计算每条样本的损失，加到result身上得到整体数据集损失
    for feature,label in zip(samples_features,samples_labels):
        #这是计算一条样本的y_predict
        p_result = p_theta_function(feature,w)
        loss_result = -1*label*np.log(p_result) - (1-label)*np.log(1-p_result)
        result += loss_result
    return result

w1_space = np.arange(w[:,0]-0.6,w[:,0]+0.6,1.2/49)
w2_space = np.arange(w[:,1]-0.6,w[:,1]+0.6,1.2/49)
w1,w2 = np.meshgrid(w1_space, w2_space)
w_list = []
for i in range(50):
    temp = []
    for j in range(50):
        temp.append(loss_fuction(x,y,np.array([w1[i][j],w2[i][j]])))
    w_list.append(temp)

result = np.array(w_list)

fig=plt.figure()
ax1 = plt.axes(projection='3d')
ax1.contour(w1,w2,result,30)
ax1.view_init(elev=90., azim=140)
plt.show()

fig=plt.figure()
ax2 = plt.axes(projection='3d')
ax2.plot_surface(w1,w2,result,rstride = 1, cstride = 1,cmap='rainbow')
ax2.view_init(elev=20., azim=140)
plt.show()


from frontend.setup import frontend
import urllib
import os

def user_picture(user_id):
    # Смотрим случай, когда есть аватарка
    try:
        # Запрашиваются первое фото из тех, какие есть у юзера
        pictureMas = frontend.get_user_profile_photos(
            user_id, limit=1).photos[0]
        # Берем айдишник получившегося фото
        file_id = pictureMas[-1].file_id
    # Случай, когда нет аватарки (мб выдавать месс, мол, поставьте аву, пж?)
    except Exception:
        if not os.path.isfile('out.jpg'):
            url = 'https://i.ibb.co/m5NDJqv/noavatar.jpg'
            f = open('out.jpg', 'wb')
            f.write(urllib.request.urlopen(url).read())
        file_id = open('out.jpg', 'rb')
    return file_id


from osv import fields, osv
import datetime

class tigernix_export(osv.osv):
    _name = 'tigernix.export'

    _columns = {
        'name'          : fields.char('Name',size=64),
        'field_line'    : fields.one2many('tigernix.export.field', 'export_id', 'Filter Field', help="The rule is satisfied if at least one test is True"),
        'filter_line'   : fields.one2many('tigernix.export.filter', 'export_id', 'Filter Field',),
        'model_id'      : fields.many2one('ir.model', 'Object',select=1),
        'field_ids'     : fields.many2many('ir.model.fields', 'export_field_rel', 'field_id', 'export_id', 'Field Selection'),
    }

tigernix_export()

class tigernix_export_field(osv.osv):
    _name = 'tigernix.export.field'
    _rec_name = 'field_id'

    _columns = {
        'export_id' : fields.many2one('tigernix.export', 'Export Root'),
        'field_id'  : fields.many2one('ir.model.fields', 'Field', domain= "[('model_id','=', parent.model_id)]",  select=1),
    }

tigernix_export_field()

class tigernix_export_filter(osv.osv):
    _name = 'tigernix.export.filter'
    _rec_name = 'field_id'

    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
        res = super(tigernix_export_filter, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)

        list = []
        if 'parent_id' in context:
            field_obj  = self.pool.get('ir.model.fields')
            field_ids  = field_obj.search(cr,uid,[('model_id','=',context['parent_id'])])
            fields    = field_obj.browse(cr,uid,field_ids)

            for field in fields:
                if field.model_id.model:
                    object = self.pool.get(field.model_id.model)
                    if field.name in object._columns:
                        if 'function' in str(object._columns[field.name]):
                            continue
                        else:
                            list.append(field.id)

        form = """ <field name="field_id" colspan="2" domain="[('model_id','=', parent.model_id),('id','in',%s),('ttype','not in',('one2many','many2many'))]" on_change="onchange_field_id(field_id)" required="1"/>  """ % list
        res['arch'] = res['arch'].replace(str("""<field name="field_id" colspan="2" domain="[('model_id','=', parent.model_id)]" on_change="onchange_field_id(field_id)" required="1"/>"""),form)

        return res

    def _overall_operand(self, cr, uid, ids, field_name, arg, context):
        res = {}
        for line in self.browse(cr, uid, ids):
            if line.type == 'boolean':
                if line.operand_bool:
                    res[line.id] = 'Yes'
                else:
                    res[line.id] = 'No'
            elif line.type == 'date':
                res[line.id] = line.operand_date
            elif line.type == 'datetime':
                res[line.id] = line.operand_datetime
            else:
                res[line.id] = line.operand
        return res

    _columns = {
        'export_id'          : fields.many2one('tigernix.export', 'Export Root'),
        'field_id'           : fields.many2one('ir.model.fields', 'Field', select=1),
        'operator'           : fields.selection((('=', '='), ('<>', '<>'), ('<=', '<='), ('>=', '>='), ('<', '<'), ('>', '>'), ('child_of', 'child_of'), ('like', 'like'), ('ilike', 'ilike'),('in', 'in')), 'Operator'),
        'type'               : fields.char('Type', size=64),
        'overall_operand'    : fields.function(_overall_operand, method=True, type='char', string='Operand', size=64),
        'operand'            : fields.char('Operand', size=64),
        'operand_bool'       : fields.boolean('Operand'),
        'operand_date'       : fields.date('Operand'),
        'operand_datetime'   : fields.datetime('Operand'),

    }

    def onchange_field_id(self,cr,uid,ids,field_id):
        result = {}
        if not field_id:
            return {'value' : {'type' : False}}
        field  = self.pool.get('ir.model.fields').browse(cr,uid,field_id)
        type   = field.ttype

        result['type']   = type
        return {'value' : result}

tigernix_export_filter()


from __future__ import unicode_literals
from __future__ import print_function

from moya.response import MoyaResponse
from moya.compat import PY2, py2bytes
from moya import http
from moya.tools import datetime_to_epoch, md5_hexdigest
from moya import logic

from fs.path import basename

from datetime import datetime
import mimetypes
import time

def serve_file(req, fs, path, name=None):
    """Serve a file"""
    res = MoyaResponse()
    mime_type, encoding = mimetypes.guess_type(basename(path))
    if mime_type is None:
        mime_type = b"application/octet-stream" if PY2 else "application/octet-stream"

    if not path or not fs.isfile(path):
        raise logic.EndLogic(http.RespondNotFound())

    serve_file = None
    try:
        file_size = fs.getsize(path)
        info = fs.getinfokeys(path, 'modified_time')
        serve_file = fs.open(path, 'rb')
    except FSError:
        if serve_file is not None:
            serve_file.close()
        raise logic.EndLogic(http.RespondNotFound())
    else:
        mtime = info.get('modified_time', None)
        if mtime is None:
            mtime = time.time()
        else:
            mtime = datetime_to_epoch(mtime)
        res.date = datetime.utcnow()
        res.content_type = py2bytes(mime_type)
        res.last_modified = mtime
        res.etag = "%i-%i-%s" % (mtime, file_size, md5_hexdigest(path))
        res.server = "Moya/1.0"
        if name is not None:
            res.content_disposition = 'attachment; filename="{}"'.format(name)

        status304 = False
        if req.if_none_match and res.etag:
            status304 = res.etag in req.if_none_match
        elif req.if_modified_since and res.last_modified:
            status304 = res.last_modified <= req.if_modified_since
        if status304:
            res.status = 304
            serve_file.close()
        else:
            res.body_file = serve_file
        res.content_length = file_size
    raise logic.EndLogic(res)


#! /usr/bin/env python

#Import timer action actions
import rospy

import actionlib
from my_actions.msg import TimerAction, TimerGoal, TimerResult

#Start node
#wait for server
#set timer goal to 5 seconds
#get and print results
rospy.init_node('timer_action_client')
client = actionlib.SimpleActionClient('timer', TimerAction)
client.wait_for_server()
goal = TimerGoal()
goal.time_to_wait = rospy.Duration.from_sec(5.0)
client.send_goal(goal)
client.wait_for_result()
print('Time elapsed: %f' % (client.get_result().time_elapsed.to_sec()))


from flask import Flask
from flask_graphql import GraphQLView

from storeify.db import get_db_session
from storeify.schema import schema

def create_app(debug=True, database=None):
    app = Flask(__name__)
    app.debug = True

    app.add_url_rule(
        '/graphql',
        view_func=GraphQLView.as_view(
            'graphql',
            schema=schema,
            graphiql=True
        )
    )

    app.teardown_appcontext

    def shutdown_session(exception=None):
        get_db_session.remove()

    return app

if __name__ == '__main__':
    app = create_app()
    app.run


import fileinput
import re
from itertools import product

def calculate(mask, place, number):
    inp = [x for x in f'{place:036b}']
    # Apply bitmask to the input
    for i, sym in enumerate(mask):
        if sym == "1" or sym == "X":
            inp[i] = sym

    # Fill in Xs with each possible combination of Os and 1s
    results = []
    possibilities = product("01", repeat=inp.count("X"))
    for possibility in possibilities:
        # possibility - tuple without a priori known length
        # use pos to navigate through "possibility" elements
        pos = 0
        output = list(inp)
        for i, sym in enumerate(inp):
            if sym == "X":
                output[i] = possibility[pos]
                pos += 1
        as_str = "".join(output)
        results.append(as_str)

    # Convert binary numbers in results to decimals
    # Write down the number to each of the address places
    for bin_number in results:
        deci_number = int(bin_number, 2)
        memory[deci_number] = number

contents = [line.strip() for line in fileinput.input()]
memory = {}

for line in contents:
    if line.startswith("mask"):
        mask = line[7:]
    else:
        matched = re.match(r'mem\[(\d+)\] = (\d+)', line)
        calculate(mask, int(matched.group(1)), int(matched.group(2)))

mem_sum = sum([value for value in memory.values()])
print(mem_sum)


def _rc_brpoplpush(self, src, dst, timeout=0):
        """
        Pop a value off the tail of ``src``, push it on the head of ``dst``
        and then return it.

        This command blocks until a value is in ``src`` or until ``timeout``
        seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
        forever.
        Not atomic
        """
        rpop = self.brpop(src, timeout)
        if rpop is not None:
            self.lpush(dst, rpop[1])
            return rpop[1]
        return None


import numpy as np
from scipy.optimize import leastsq,curve_fit

class FE(object):
    def getTitle(self):
        return  [
            "mean",
            "var",
            "calculus",
            "maxAbs",
            "deltaT",
            "median",
            "gradientF",
            "gradientS",
            "bS",
            "label"
        ]

    def extract(self,list):
        r = [
            self.__mean(list),
            self.__var(list),
            self.__sum(list),
            self.__maxAbs(list),
            self.__deltaT(list),
            self.__median(list)
        ]
        for i in self.__fitting(list):
            r.append(i)
        return r

    def __mean(self,list):
        return np.mean(list)

    def __var(self,list):
        return np.var(list)

    def __sum(self,list):
        return sum(list)

    def __maxAbs(self,list):
        s = self.__sum(list)
        if s <= 0:
            return abs(min(list))
        else:
            return max(list)

    def __deltaT(self,list):
        return len(list)

    def __median(self,list):
        return np.median(list)

    def __fitting(self,l):
        func = lambda x,a,b: a*x+b
        residuals = lambda p, y, x: y-func(x,p)
        s = self.__sum(l)
        l = l.tolist()
        if s <= 0:
            i = l.index(min(l))
        else:
            i = l.index(max(l))

        frt = np.array(l[:i])
        snd = np.array(l[i:])
        if len(frt)<5:
            opt_parmsF = [0.0,0.0]
        else:
            opt_parmsF, parm_covF = curve_fit(func, [i for i in xrange(len(frt))], frt)
        if len(snd)<5:
            opt_parmsS = [0.0,0.0]
        else:
            opt_parmsS, parm_covS = curve_fit(func, [i for i in xrange(len(snd))], snd)
        return opt_parmsF[0], opt_parmsS[0], opt_parmsS[1]


import sys
import os
import socket
import string
import select

#ip_serv = sys.argv[1]
ip_serv = "192.168.2.4"
#puerto = int(sys.argv[2])
puerto = 1025
address_serv = (ip_serv,puerto)

"""
if(len(sys.argv) != 3):
    print("El numero de parametros pasados no es el correcto\n")
    sys.exit()
"""

if (puerto < 1024 or puerto > 5000):
    print("El puerto que se debe elegir debe estar comprendido en el rango 1024-5000")
    sys.exit()

try:
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except:
    print("Error en la creación del socket.\n")

client_socket.connect(address_serv)

Control_sockets = [sys.stdin, client_socket]

while True:
    print(">>:")
    lectura, _ , _ = select.select(Control_sockets, [],[])

    for s in lectura:

        if s == client_socket:
            datos = s.recv(1024)
            if not datos:
                print("Conexion cerrada por el servidor")
                sys.exit()
            else:
                print("<<: ", datos.decode())
        else:
            mensaje = sys.stdin.readline()
            client_socket.send(mensaje.encode())


import os.path as osp
import os
import re
import pandas as pd
import numpy as np
from celldom.app.overview import data
from celldom import io as celldom_io
from celldom import annotation
from skimage import io as sk_io
import logging
logger = logging.getLogger(__name__)

class Dataset(object):

    def __init__(self, df, name):
        self.df = df
        self.name = name

    def select(self, rows):
        """Plotly-specific row selection"""
        return self.df if not rows else self.df.iloc[rows]

class Datasets(object):

    def __init__(self, loaders):
        self.loaders = loaders
        self.data = {}

    def __getattr__(self, dataset):
        if dataset not in self.data:
            self.data[dataset] = self.load(dataset)
        return self.data[dataset]

    def __getitem__(self, dataset):
        return self.__getattr__(dataset)

    def load(self, dataset):
        return Dataset(self.loaders[dataset](), dataset)

    def update(self, dataset, code):
        if code is None or not code.strip():
            return self[dataset]
        df = self.load(dataset).df.copy()
        logger.debug('Applying custom code to dataset "%s":\n%s', dataset, code)
        local_vars = {'df': df}
        exec(code, globals(), local_vars)
        df = local_vars['df']
        self.data[dataset] = Dataset(df, dataset)
        return self.data[dataset]

def get_array_graph_figure(df, metric, enable_normalize, value_range=None, agg_func=np.median, fill_value=None,
                           date_field=None, elapsed_field=None):
    fig_data = []

    if value_range is None:
        value_range = df[metric].min(), df[metric].max()

    # Group data based on date field, if possible
    if date_field is None:
        groups = [('', df)]
    else:
        if elapsed_field:
            groups = df.groupby([date_field, elapsed_field])
        else:
            groups = df.groupby(date_field)

    for i, (k, g) in enumerate(groups):

        gp = g.pivot_table(
            index=['st_num'], columns=['apt_num'], values=metric,
            aggfunc=agg_func, fill_value=fill_value
        )
        name = str(k)
        if len(k) == 2:
            name = '{} (hr {:.0f})'.format(*k)
        trace = dict(
            visible=False,
            y=['st {}'.format(v) for v in gp.index],
            x=['apt {}'.format(v) for v in gp.columns],
            z=gp.values,
            type='heatmap',
            colorscale='Portland',
            name=name
        )
        if enable_normalize:
            trace['zmin'] = value_range[0]
            trace['zmax'] = value_range[1]
        fig_data.append(trace)

    if len(fig_data) > 0:
        fig_data[0]['visible'] = True

    fig_layout = dict(margin=dict(b=60, t=53))

    # Add sliders for heatmaps available over time, if possible
    if date_field is not None and len(fig_data) > 0:
        # See: https://plot.ly/python/reference/#layout-updatemenus
        steps = []
        for i in range(len(fig_data)):
            step = dict(
                method='restyle',
                args=['visible', [False] * len(fig_data)],
                label=fig_data[i]['name']
            )
            step['args'][1][i] = True
            steps.append(step)

        sliders = [dict(
            active=0,
            currentvalue={"prefix": "Date: "},
            pad={"t": 75},
            steps=steps
        )]

        fig_layout['sliders'] = sliders

    return dict(data=fig_data, layout=fig_layout)

def _normalize_path(p):
    p = re.sub(r'[^ ;_.:\-a-zA-Z0-9]', '', p)
    return re.sub(r'[:; ]', '-', p)

def export_apartment_annots(exp_config, identifier, images, titles, relpath=osp.join('export', 'apartment')):
    # First export all the images as png
    export_dir, paths = export_apartment_images(identifier, images, titles, type='png', relpath=relpath)

    # Run cell detection on the exported images
    logger.info('Beginning cell detection and XML doc generation for %s files', len(paths))
    docs = annotation.generate(paths, exp_config)

    # Export XML alongside exported pngs
    logger.info('Writing XML annotation files to path "%s"', export_dir)
    annotation.export(docs, export_dir, copy=False)
    return export_dir, docs

def export_apartment_images(identifier, images, titles, type='tif', relpath=osp.join('export', 'apartment')):
    identifier = _normalize_path(identifier)
    if len(images) != len(titles):
        raise ValueError(
            'Titles list length ({}) does not match image list length ({})'
            .format(len(titles), len(images)))

    # Create path to export files (just tif for now)
    exp_dir = data.get_output_path(relpath)
    if not osp.exists(exp_dir):
        os.makedirs(exp_dir, exist_ok=True)

    # If exporting a tif, save a single image file as a stack of images
    if type == 'tif':
        path = osp.join(exp_dir, identifier + '.tif')
        celldom_io.save_tiff(path, images, titles)
        paths = [path]
    # If exporting pngs, save each image separately
    elif type == 'png':
        paths = []
        for image, title in zip(images, titles):
            path = osp.join(exp_dir, identifier + '-' + _normalize_path(title) + '.png')
            sk_io.imsave(path, image)
            paths.append(path)
    else:
        raise ValueError('Export type "{}" not supported'.format(type))
    return exp_dir, paths