import matplotlib as mpl
import matplotlib.pyplot as plt
import torch
import numpy as np
from .wrappers import FrozenLakeWrapper
from . import Actions

from mango.mango import Mango
from mango.protocols import ObsType
from mango.policies import DQNetPolicy

def plot_grid(env: FrozenLakeWrapper, cell_shape: tuple[int, int], alpha=0.2):
    grid_shape = env.unwrapped.desc.shape
    pixels_in_square = env.unwrapped.cell_size
    pixels_in_cell = tuple(s * c for s, c in zip(cell_shape, pixels_in_square))

    offset = tuple(int(s * 0.2) for s in pixels_in_square)
    width, height = tuple(int(c - 0.4 * s) for s, c in zip(pixels_in_square, pixels_in_cell))
    for x in range(grid_shape[0] // cell_shape[0]):
        for y in range(grid_shape[1] // cell_shape[1]):
            position = tuple(p * c + o for p, c, o in zip((x, y), pixels_in_cell, offset))
            plt.gca().add_patch(
                plt.Rectangle(position, width, height, fc="red", alpha=alpha)  # type: ignore
            )

def plot_trajectory(trajectory: list[ObsType] | list[int], env: FrozenLakeWrapper):
    # TODO: fix type hints, this only works for wrappers that implement the observation_inv property
    if trajectory:
        if not isinstance(trajectory[0], int):
            trajectory = [env.observation_inv(obs) for obs in trajectory]  # type: ignore
        square = env.unwrapped.cell_size
        for start_obs, next_obs in zip(trajectory[:-1], trajectory[1:]):
            y1, x1 = np.unravel_index(start_obs, env.unwrapped.desc.shape)  # type: ignore
            y2, x2 = np.unravel_index(next_obs, env.unwrapped.desc.shape)  # type: ignore
            plt.plot(
                [x1 * square[1] + square[1] // 2, x2 * square[1] + square[1] // 2],
                [y1 * square[0] + square[0] // 2, y2 * square[0] + square[0] // 2],
                "k--",
            )

def all_observations(env: FrozenLakeWrapper, mask=lambda x: x) -> tuple[list[ObsType], list[bool]]:
    # TODO: fix type hints, this only works for observation wrappers
    s = env.unwrapped.s
    obs_list = []
    valid_mask = []
    y_matrix, x_matrix = np.indices((env.unwrapped.nrow, env.unwrapped.ncol))
    for y, x in zip(y_matrix.flatten(), x_matrix.flatten()):
        env.unwrapped.s = int(y * env.unwrapped.ncol + x)
        obs_list.append(mask(env.observation(env.unwrapped.s)))  # type: ignore
        valid_mask.append(
            not (env.unwrapped.desc[y, x] == b"H" or env.unwrapped.desc[y, x] == b"G")
        )
    env.unwrapped.s = s
    return obs_list, valid_mask

def get_qval(policy: DQNetPolicy, obs_list: list[ObsType]) -> tuple[np.ndarray, np.ndarray]:
    with torch.no_grad():
        best_qvals, actions = policy.qvalues(np.stack(obs_list), batched=True).max(dim=1)  # type: ignore
    return best_qvals.cpu().detach().numpy(), actions.cpu().detach().numpy()

def plot_qval_heatmap(
    policy: DQNetPolicy, all_obs_list: tuple[list[ObsType], list[bool]], env, vmin=-1, vmax=1
):
    obs_list, valid_mask = all_obs_list
    best_qvals, actions = get_qval(policy, obs_list)
    best_qvals[~np.array(valid_mask, dtype=bool)] = np.nan
    best_qvals = np.array(best_qvals).reshape(env.unwrapped.desc.shape)

    cmap = mpl.colormaps.get_cmap("RdYlGn")  # type: ignore
    cmap.set_bad(color="aqua")
    plt.imshow(best_qvals, cmap=cmap, vmin=vmin, vmax=vmax)
    plt.colorbar()
    Y, X = np.indices(env.unwrapped.desc.shape)
    for y, x, act, is_valid in zip(Y.flatten(), X.flatten(), actions, valid_mask):
        if is_valid:
            dy, dx = Actions.to_delta(act)
            # draw arrows for actions in the middle of the cell
            plt.annotate(
                "",
                xy=(x + 0.4 * dx, y + 0.4 * dy),
                xytext=(x - 0.4 * dx, y - 0.4 * dy),
                arrowprops=dict(width=1, headwidth=3, headlength=3),
            )
    return best_qvals

def plot_all_abstractions(mango: Mango, trajectory: list[ObsType] | list[int] = []):
    # TODO: fix type hints, this only works for specific Mango instances
    env: FrozenLakeWrapper = mango.environment.environment  # type: ignore
    plt.figure(figsize=(3 * len(mango.abstract_layers) + 3, 3))
    plt.subplot(1, len(mango.abstract_layers) + 1, 1)
    plt.title(f"Environment")
    plt.imshow(env.unwrapped.render())
    plot_trajectory(trajectory, env)
    plt.xticks([])
    plt.yticks([])
    for col, layer in enumerate(mango.abstract_layers, start=1):
        plt.subplot(1, len(mango.abstract_layers) + 1, col + 1)
        plt.title(f"Layer {col+1} Abstraction")
        plt.imshow(env.render())  # type: ignore
        plot_trajectory(trajectory, env)
        plot_grid(env, layer.abs_actions.cell_shape)  # type: ignore
        plt.xticks([])
        plt.yticks([])

def plot_all_qvals(mango: Mango, trajectory: list[ObsType] | list[int] = [], size=3):
    # TODO: fix type hints, this only works for specific Mango instances
    env: FrozenLakeWrapper = mango.environment.environment  # type: ignore
    plt.figure(figsize=((size + 1) * len(Actions) + size, size * len(mango.abstract_layers)))
    n_rows, n_cols = len(mango.abstract_layers), len(Actions) + 1
    for row, layer in enumerate(mango.abstract_layers):
        plt.subplot(n_rows, n_cols, row * (len(Actions) + 1) + 1)
        plt.title(f"Layer {row+1} Abstraction")
        plt.imshow(env.unwrapped.render())
        plot_trajectory(trajectory, env)
        plot_grid(env, layer.abs_actions.cell_shape)  # type: ignore
        plt.xticks([])
        plt.yticks([])
        for col, action in enumerate(Actions, start=2):
            plt.subplot(n_rows, n_cols, row * (len(Actions) + 1) + col)
            plt.title(f"Qvals AbsAction {action.name}")
            policy = layer.policy.policies[action]  # type: ignore
            all_obs_list = all_observations(env, lambda obs: layer.abs_actions.mask(action, obs))  # type: ignore
            plot_qval_heatmap(policy, all_obs_list, env)
            plt.xticks([])
            plt.yticks([])


import datetime
import logging
import os
import pytz
import sys
import requests
import time

if __name__ == '__main__':
    sys.path.append(os.environ.get('XLRINFRADIR', ''))

from py_common.env_configuration import EnvConfiguration
cfg = EnvConfiguration({'LOG_LEVEL': {'default': logging.INFO},
                        'BACKEND_HOST': {'required': True},
                        'BACKEND_PORT': {'required': True},
                        'TIMEZONE': {'default': 'America/Los_Angeles'}})

from flask import Flask, request
from flask import render_template, make_response, jsonify
from flask_cors import CORS, cross_origin

# It's log, it's log... :)
logging.basicConfig(
                level=cfg.get('LOG_LEVEL'),
                format="'%(asctime)s - %(threadName)s - %(funcName)s - %(levelname)s - %(message)s",
                handlers=[logging.StreamHandler()])
logger = logging.getLogger(__name__)

timezone = pytz.timezone(cfg.get('TIMEZONE'))

app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'

methods=['GET']
@app.route('/', methods=methods)
@cross_origin()
def test_connection():
    """
    / should return 200 ok.
    """
    return "Connection check A-OK!"

@app.route('/jdq', methods=methods)
@cross_origin()
def jenkins_data_query_index():
    # XXXrs - WORKING HERE - want to put up a page with options that
    #         click through to other places here...
    return render_template("jmd_index.html")

def _get_jobs_data(*, start, end, host=None):
    args = "start={}&end={}".format(start, end)
    if host is not None:
        args += "&host={}".format(host)
    back_url = "http://{}:{}/jenkins_builds_by_time?{}"\
               .format(cfg.get('BACKEND_HOST'), cfg.get('BACKEND_PORT'), args)
    response = requests.get(back_url, verify=False) # XXXrs disable verify!
    rsp = response.json()
    logger.info("rsp {}".format(rsp))
    logger.info("rsp length: {}".format(len(rsp)))

    for item in rsp['jobs']:
        start_time_ms = item.get('start_time_ms', None)
        if not start_time_ms:
            fmt = "00/00/00 00:00:00"
        else:
            fmt = datetime.datetime.fromtimestamp(start_time_ms/1000, tz=timezone).strftime("%Y/%m/%d %H:%M:%S")
        item["start_time_fmt"] = fmt

        item["duration_s"] = int(item.get('duration_ms', 0)/1000)
    return rsp['jobs']

DAY = 60*60*24
WEEK = DAY*7

@app.route('/jenkins_builds_by_time', methods=methods)
@cross_origin()
def jenkins_builds_by_time():
    now = int(time.time())
    start = request.args.get('start', 0)
    end = request.args.get('end', now)
    host = request.args.get('host', None)
    jobs = _get_jobs_data(start=start, end=end, host=host)
    return render_template("jobs_table.html", jobs=jobs)

@app.route('/jenkins_jobs_last_1w', methods=methods)
@cross_origin()
def jenkins_jobs_last_1w():
    now = int(time.time())
    start = now-(WEEK)
    host = request.args.get('host', None)
    jobs = _get_jobs_data(start=start, end=now, host=host)
    return render_template("jobs_table.html", jobs=jobs)

@app.route('/jenkins_jobs_last_2w', methods=methods)
@cross_origin()
def jenkins_jobs_last_2w():
    now = int(time.time())
    start = now-(2*WEEK)
    host = request.args.get('host', None)
    jobs = _get_jobs_data(start=start, end=now, host=host)
    return render_template("jobs_table.html", jobs=jobs)

@app.route('/jenkins_jobs_last_30d', methods=methods)
@cross_origin()
def jenkins_jobs_last_30d():
    now = int(time.time())
    start = now-(30*DAY)
    host = request.args.get('host', None)
    jobs = _get_jobs_data(start=start, end=now, host=host)
    return render_template("jobs_table.html", jobs=jobs)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=4001, debug=True)


import math
import torch
from typing import Set
from torch import Tensor
import torch.nn.functional as F

from torchhd.tensors.base import VSATensor

type_conversion = {
    torch.complex64: torch.float32,
    torch.complex128: torch.float64,
}

class FHRRTensor(VSATensor):
    """Fourier Holographic Reduced Representation

    Proposed in `Holographic Reduced Representation: Distributed Representation for Cognitive Structures <https://philpapers.org/rec/PLAHRR/>`_, this model uses complex phaser hypervectors.
    """

    supported_dtypes: Set[torch.dtype] = {torch.complex64, torch.complex128}

    @classmethod
    def empty(
        cls,
        num_vectors: int,
        dimensions: int,
        *,
        dtype=torch.complex64,
        device=None,
        requires_grad=False,
    ) -> "FHRRTensor":
        """Creates a set of hypervectors representing empty sets.

        When bundled with a random-hypervector :math:`x`, the result is :math:`x`.
        The empty vector of the FHRR model is a set of 0 values in both real and imaginary part.

        Args:
            num_vectors (int): the number of hypervectors to generate.
            dimensions (int): the dimensionality of the hypervectors.
            dtype (``torch.dtype``, optional): the desired data type of returned tensor. Default: if ``None`` is torch.complex64.
            device (``torch.device``, optional):  the desired device of returned tensor. Default: if ``None``, uses the current device for the default tensor type (see torch.set_default_tensor_type()). ``device`` will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types.
            requires_grad (bool, optional): If autograd should record operations on the returned tensor. Default: ``False``.

        Examples::

            >>> torchhd.FHRRTensor.empty(3, 6)
            FHRR([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
                    [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
                    [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

            >>> torchhd.FHRRTensor.empty(3, 6, dtype=torch.complex128)
            FHRR([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
                    [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
                    [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],
                   dtype=torch.complex128)
        """
        if dtype is None:
            dtype = torch.complex64

        if dtype not in cls.supported_dtypes:
            name = cls.__name__
            options = ", ".join([str(x) for x in cls.supported_dtypes])
            raise ValueError(f"{name} vectors must be one of dtype {options}.")

        result = torch.zeros(
            num_vectors,
            dimensions,
            dtype=dtype,
            device=device,
            requires_grad=requires_grad,
        )
        return result.as_subclass(cls)

    @classmethod
    def identity(
        cls,
        num_vectors: int,
        dimensions: int,
        *,
        dtype=torch.complex64,
        device=None,
        requires_grad=False,
    ) -> "FHRRTensor":
        """Creates a set of identity hypervectors.

        When bound with a random-hypervector :math:`x`, the result is :math:`x`.

        Args:
            num_vectors (int): the number of hypervectors to generate.
            dimensions (int): the dimensionality of the hypervectors.
            dtype (``torch.dtype``, optional): the desired data type of returned tensor. Default: if ``None`` is torch.complex64.
            device (``torch.device``, optional):  the desired device of returned tensor. Default: if ``None``, uses the current device for the default tensor type (see torch.set_default_tensor_type()). ``device`` will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types.
            requires_grad (bool, optional): If autograd should record operations on the returned tensor. Default: ``False``.

        Examples::

            >>> torchhd.FHRRTensor.identity(3, 6)
            FHRR([[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j],
                    [1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j],
                    [1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]])

            >>> torchhd.FHRRTensor.identity(3, 6, dtype=torch.complex128)
            FHRR([[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j],
                    [1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j],
                    [1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]],
                   dtype=torch.complex128)

        """
        if dtype is None:
            dtype = torch.complex64

        if dtype not in cls.supported_dtypes:
            name = cls.__name__
            options = ", ".join([str(x) for x in cls.supported_dtypes])
            raise ValueError(f"{name} vectors must be one of dtype {options}.")

        result = torch.ones(
            num_vectors,
            dimensions,
            dtype=dtype,
            device=device,
            requires_grad=requires_grad,
        )
        return result.as_subclass(cls)

    @classmethod
    def random(
        cls,
        num_vectors: int,
        dimensions: int,
        *,
        dtype=torch.complex64,
        device=None,
        requires_grad=False,
        generator=None,
    ) -> "FHRRTensor":
        """Creates a set of random independent hypervectors.

        The resulting hypervectors are sampled uniformly at random from the ``angle`` between -pi and +pi.

        Args:
            num_vectors (int): the number of hypervectors to generate.
            dimensions (int): the dimensionality of the hypervectors.
            generator (``torch.Generator``, optional): a pseudorandom number generator for sampling.
            dtype (``torch.dtype``, optional): the desired data type of returned tensor. Default: if ``None`` is torch.complex64.
            device (``torch.device``, optional):  the desired device of returned tensor. Default: if ``None``, uses the current device for the default tensor type (see torch.set_default_tensor_type()). ``device`` will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types.
            requires_grad (bool, optional): If autograd should record operations on the returned tensor. Default: ``False``.

        Examples::

            >>> torchhd.FHRRTensor.random(3, 6)
            FHRR([[ 0.2082-0.9781j,  0.7038-0.7104j, -0.6297-0.7768j, -0.9632-0.2689j, -0.4941+0.8694j,  0.9771-0.2128j],
                    [ 0.9820-0.1887j, -0.0395+0.9992j, -0.5139+0.8579j, -0.8415-0.5402j, -0.6696-0.7427j,  0.2312+0.9729j],
                    [-0.9786+0.2057j,  0.1714+0.9852j, -0.5925-0.8056j, -0.5698-0.8218j, -0.4632-0.8863j,  0.6996-0.7145j]])

            >>> torchhd.FHRRTensor.random(3, 6, dtype=torch.long)
            FHRR([[-0.9996-0.0285j, -0.0688-0.9976j,  0.6900-0.7238j,  0.9519-0.3064j, 0.8131-0.5821j,  0.9942-0.1077j],
                    [-0.9199-0.3922j,  0.8073-0.5902j,  0.8683+0.4960j,  0.1250+0.9922j, 0.6248+0.7808j, -0.2495+0.9684j],
                    [ 0.0178+0.9998j, -0.3006-0.9538j, -0.9346+0.3557j,  0.9017-0.4324j, 0.4029-0.9153j,  0.4818-0.8763j]],
                    dtype=torch.complex128)

        """
        if dtype is None:
            dtype = torch.complex64

        if dtype not in cls.supported_dtypes:
            name = cls.__name__
            options = ", ".join([str(x) for x in cls.supported_dtypes])
            raise ValueError(f"{name} vectors must be one of dtype {options}.")

        dtype = type_conversion[dtype]

        size = (num_vectors, dimensions)
        angle = torch.empty(size, dtype=dtype, device=device)
        angle.uniform_(-math.pi, math.pi, generator=generator)

        result = torch.complex(angle.cos(), angle.sin())
        result.requires_grad = requires_grad
        return result.as_subclass(cls)

    def bundle(self, other: "FHRRTensor") -> "FHRRTensor":
        r"""Bundle the hypervector with other using element-wise sum.

        This produces a hypervector maximally similar to both.

        The bundling operation is used to aggregate information into a single hypervector.

        Args:
            other (FHRR): other input hypervector

        Shapes:
            - Self: :math:`(*)`
            - Other: :math:`(*)`
            - Output: :math:`(*)`

        Examples::

            >>> a, b = torchhd.FHRRTensor.random(2, 6)
            >>> a
            FHRR([ 0.9556-0.2948j,  0.1746+0.9846j, -0.6270-0.7790j, -0.2423-0.9702j, 0.6358+0.7719j,  0.9965-0.0834j])
            >>> b
            FHRR([-0.9539-0.3000j, -0.1279+0.9918j, -0.4610+0.8874j, -0.3638-0.9315j, 0.9554+0.2952j,  0.8659+0.5003j])
            >>> a.bundle(b)
            FHRR([-1.6885+0.4104j, -0.4094-1.4874j,  0.0090-0.0058j,  0.1039-0.9365j, 0.0413-1.8657j,  0.6276+1.8385j])

            >>> a, b = torchhd.FHRRTensor.random(2, 10, dtype=torch.complex128)
            >>> a
            FHRR([ 0.4521-0.8920j,  0.7917-0.6109j,  0.5414-0.8408j, -0.9550-0.2967j, 0.9320+0.3626j, -0.8509-0.5253j],
            dtype=torch.complex128)
            >>> b
            FHRR([ 0.6954-0.7186j, -0.5621-0.8270j,  0.4685+0.8835j, -0.9319+0.3627j, -0.8310-0.5563j,  0.2545+0.9671j],
            dtype=torch.complex128)
            >>> a.bundle(b)
            FHRR([ 1.1475-1.6106j,  0.2296-1.4379j,  1.0099+0.0427j, -1.8869+0.0660j, 0.1010-0.1937j, -0.5964+0.4417j],
            dtype=torch.complex128)

        """
        return torch.add(self, other)

    def multibundle(self) -> "FHRRTensor":
        """Bundle multiple hypervectors"""
        return torch.sum(self, dim=-2, dtype=self.dtype)

    def bind(self, other: "FHRRTensor") -> "FHRRTensor":
        r"""Bind the hypervector with other using element-wise multiplication.

        This produces a hypervector dissimilar to both.

        Binding is used to associate information, for instance, to assign values to variables.

        Args:
            other (FHRR): other input hypervector

        Shapes:
            - Self: :math:`(*)`
            - Other: :math:`(*)`
            - Output: :math:`(*)`

        Examples::

            >>> a, b = torchhd.FHRRTensor.random(2, 6)
            >>> a
            FHRR([ 0.9317-0.3632j,  0.7320+0.6813j, -0.8588+0.5123j, -0.9723-0.2339j, -0.9631-0.2692j, -0.4093-0.9124j])
            >>> b
            FHRR([ 0.9983+0.0578j, -0.5043-0.8635j,  0.5505-0.8349j,  0.9805+0.1966j, 0.9656+0.2599j, -0.5609-0.8279j])
            >>> a.bind(b)
            FHRR([ 0.9511-0.3087j,  0.2191-0.9757j, -0.0450+0.9990j, -0.9073-0.4204j, -0.8600-0.5102j, -0.5257+0.8507j])

            >>> a, b = torchhd.FHRRTensor.random(2, 6, dtype=torch.complex128)
            >>> a
            FHRR([ 0.7838-0.6210j, -0.0258+0.9997j,  0.0263+0.9997j,  0.9617+0.2742j, 0.1281-0.9918j, -0.4321+0.9018j],
            dtype=torch.complex128)
            >>> b
            FHRR([-0.9995+0.0308j,  0.4550-0.8905j,  0.2793+0.9602j,  0.0025-1.0000j, 0.4470+0.8946j,  0.8314-0.5557j],
            dtype=torch.complex128)
            >>> a.bind(b)
            FHRR([-0.7643+0.6449j,  0.8785+0.4778j, -0.9525+0.3045j,  0.2766-0.9610j, 0.9444-0.3287j,  0.1419+0.9899j],
            dtype=torch.complex128)

        """
        return torch.mul(self, other)

    def multibind(self) -> "FHRRTensor":
        """Bind multiple hypervectors"""
        return torch.prod(self, dim=-2, dtype=self.dtype)

    def inverse(self) -> "FHRRTensor":
        r"""Invert the hypervector for binding.

        For FHRR the inverse of hypervector is its conjugate, this returns the conjugate of the hypervector.

        Shapes:
            - Self: :math:`(*)`
            - Output: :math:`(*)`

        Examples::

            >>> a = torchhd.FHRRTensor.random(1, 6)
            >>> a
            FHRR([[ 0.9855+0.1698j, -0.0927-0.9957j, -0.8316-0.5554j, -0.1433-0.9897j, 0.8328+0.5536j,  0.2071+0.9783j]])
            >>> a.inverse()
            FHRR([[ 0.9855-0.1698j, -0.0927+0.9957j, -0.8316+0.5554j, -0.1433+0.9897j, 0.8328-0.5536j,  0.2071-0.9783j]])

            >>> a = torchhd.FHRRTensor.random(1, 6, dtype=torch.complex128)
            >>> a
            FHRR([[-0.9983-0.0574j, -0.4825+0.8759j,  0.9631-0.2692j,  0.9066-0.4219j, 0.7099-0.7044j, -0.1313-0.9913j]],
            dtype=torch.complex128)
            >>> a.inverse()
            >>> a.inverse()
            FHRR([[-0.9983+0.0574j, -0.4825-0.8759j,  0.9631+0.2692j,  0.9066+0.4219j, 0.7099+0.7044j, -0.1313+0.9913j]],
            dtype=torch.complex128)

        """
        # Resolve conj to ensure the the returned tensor does not share the same memory
        return torch.conj(self).resolve_conj()

    def negative(self) -> "FHRRTensor":
        r"""Negate the hypervector for the bundling inverse.

        Shapes:
            - Self: :math:`(*)`
            - Output: :math:`(*)`

        Examples::

            >>> a = torchhd.FHRRTensor.random(1, 6)
            >>> a
            FHRR([[-0.0187-0.9998j,  0.1950-0.9808j,  0.5203+0.8540j,  0.8587+0.5124j, 0.9998+0.0203j, -0.6237-0.7816j]])
            >>> a.negative()
            FHRR([[ 0.0187+0.9998j, -0.1950+0.9808j, -0.5203-0.8540j, -0.8587-0.5124j, -0.9998-0.0203j,  0.6237+0.7816j]])

            >>> a = torchhd.FHRRTensor.random(1, 6, dtype=torch.complex128)
            >>> a
            FHRR([[ 0.8255+0.5644j, -0.8352-0.5500j,  0.9751-0.2218j, -0.9808-0.1950j, -0.3840-0.9233j,  0.4106-0.9118j]],
            dtype=torch.complex128)
            >>> a.negative()
            FHRR([[-0.8255-0.5644j,  0.8352+0.5500j, -0.9751+0.2218j,  0.9808+0.1950j, 0.3840+0.9233j, -0.4106+0.9118j]], dtype=torch.complex128)

        """
        return torch.negative(self)

    def permute(self, shifts: int = 1) -> "FHRRTensor":
        r"""Permute the hypervector.

        The permutation operator is used to assign an order to hypervectors.

        Args:
            shifts (int, optional): The number of places by which the elements of the tensor are shifted.

        Shapes:
            - Self: :math:`(*)`
            - Output: :math:`(*)`

        Examples::

            >>> a = torchhd.FHRRTensor.random(1, 6)
            >>> a
            FHRR([[-0.3286-0.9445j,  0.2161-0.9764j, -0.6484+0.7613j, -0.4020+0.9156j, 0.8282-0.5605j, -0.9869+0.1613j]])
            >>> a.permute()
            FHRR([[-0.9869+0.1613j, -0.3286-0.9445j,  0.2161-0.9764j, -0.6484+0.7613j, -0.4020+0.9156j,  0.8282-0.5605j]])

            >>> a = torchhd.FHRRTensor.random(1, 6, dtype=torch.complex128)
            >>> a
            FHRR([[-0.9500-0.3123j, -0.0234+0.9997j, -0.1071-0.9943j, -0.8558-0.5174j, 0.9631-0.2690j,  0.5470-0.8371j]],
            dtype=torch.complex128)
            >>> a.permute()
            FHRR([[ 0.5470-0.8371j, -0.9500-0.3123j, -0.0234+0.9997j, -0.1071-0.9943j, -0.8558-0.5174j,  0.9631-0.2690j]],
            dtype=torch.complex128)

        """
        return torch.roll(self, shifts=shifts, dims=-1)

    def dot_similarity(self, others: "FHRRTensor") -> Tensor:
        """Inner product with other hypervectors"""
        if others.dim() >= 2:
            others = others.transpose(-2, -1)
        return torch.real(torch.matmul(self, torch.conj(others)))

    def cosine_similarity(self, others: "FHRRTensor", *, eps=1e-08) -> Tensor:
        """Cosine similarity with other hypervectors"""
        self_dot = torch.sum(torch.real(self * torch.conj(self)), dim=-1)
        self_mag = torch.sqrt(self_dot)

        others_dot = torch.sum(torch.real(others * torch.conj(others)), dim=-1)
        others_mag = torch.sqrt(others_dot)

        if self.dim() >= 2:
            magnitude = self_mag.unsqueeze(-1) * others_mag.unsqueeze(-2)
        else:
            magnitude = self_mag * others_mag

        magnitude = torch.clamp(magnitude, min=eps)
        return self.dot_similarity(others) / magnitude


import sublime
import sublime_plugin

from FuzzyFilePath.project.ProjectManager import ProjectManager
from FuzzyFilePath.project.ProjectManager import get_project_folder

ID = "ProjectListener"

class ProjectListener(sublime_plugin.EventListener):
    """ listens on window changes, delegating events to project manager """

    previous_project = None
    previous_window = None

    def on_activated(self, view):
        window = view.window()
        if not window:
            return False

        project_folder = get_project_folder(window)

        if self.previous_project != project_folder:
            if self.previous_project is not None:
                self.on_project_activated(view)
            self.previous_project = project_folder

        elif self.previous_window is not sublime.active_window().id():
            self.previous_window = sublime.active_window().id()
            self.on_window_activated(view)

    # project has been refocused
    def on_window_activated(self, view):
        ProjectManager.update_project(view.window())

    # another (possible) project has been opened/focused
    def on_project_activated(self, view):
        ProjectManager.activate_project(view.window())


# © 2021 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo.tests.common import SavepointCase

class TestResPartner(SavepointCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.client_type = cls.env["client.type"].create({"name": "Type A"})
        cls.parent = cls.env["res.partner"].create(
            {
                "name": "Parent",
                "is_company": True,
            }
        )
        cls.contact = cls.env["res.partner"].create(
            {
                "name": "Contact",
                "parent_id": cls.parent.id,
            }
        )

    def test_client_type_propagated_to_contact(self):
        self.parent.client_type_ids = self.client_type
        assert self.contact.client_type_ids == self.client_type


import os
import csv

# Input directory containing individual CSV files
input_dir = "csv"

# Output combined CSV file
output_file = "U_SRG-STIG_Library.csv"

# Initialize a flag to check if the header has been written
header_written = False

# Open the output CSV file for writing
with open(output_file, "w", newline="") as combined_csv_file:
    csv_writer = csv.writer(combined_csv_file)

    # Iterate through each CSV file in the input directory
    for filename in os.listdir(input_dir):
        if filename.endswith(".csv"):
            csv_file_path = os.path.join(input_dir, filename)

            # Open and read the current CSV file
            with open(csv_file_path, "r") as current_csv_file:
                csv_reader = csv.reader(current_csv_file)

                # Skip the header row if it has already been written
                if header_written:
                    next(csv_reader)  # Skip the header row

                # Iterate through the rows in the current CSV and write to the combined CSV
                for row in csv_reader:
                    csv_writer.writerow(row)

            # Set the header_written flag to True after writing the header once
            header_written = True

print(f"Combined CSV file '{output_file}' has been created.")


import math

def binary_search(seq, target):
	low = 0
	high = len(seq) - 1

	while low <= high:

		mid = low + (high - low) / 2

		mid = int(math.floor(mid))

		if (seq[mid] == target):
			return mid
		elif (seq[mid] < target):
			low = mid + 1
		else:
			high = mid - 1

A = [1, 3, 5, 7, 9, 11, 13, 14, 15, 16, 25, 43, 68, 71, 73, 75]
target = 68

target_index = binary_search(A, target)

print("Target: " + str(target))
print("Sequance: " + str(A))
print("Target_index: " + str(target_index))
print("Target in the array: " + str(A[target_index]))


import sys, os
import numpy as np

import emcee
import corner
import scipy.optimize as op
import scipy.interpolate as interp
from scipy.constants import speed_of_light
from numpy.linalg import inv
import itertools

import nbodykit.lab
import nbodykit
import pickle

import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator

base_dir = '/path/to/data/'
output_path = base_dir
zmin = 0.5
zmax = 0.75
zmean = 0.61 # see calculation in read_data()
kmin = 0.01
kmax = 0.3

# Download the data and corresponding random BOSS dataset (Two files for North and South)
# wget -N https://data.sdss.org/sas/dr12/boss/lss/galaxy_DR12v5_CMASSLOWZTOT_North.fits.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/galaxy_DR12v5_CMASSLOWZTOT_South.fits.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/random0_DR12v5_CMASSLOWZTOT_North.fits.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/random0_DR12v5_CMASSLOWZTOT_South.fits.gz -P path/to/folder/

# Download the simulated BOSS datasets
# wget -N https://data.sdss.org/sas/dr12/boss/lss/dr12_multidark_patchy_mocks/Patchy-Mocks-DR12SGC-COMPSAM_V6C.tar.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/dr12_multidark_patchy_mocks/Patchy-Mocks-DR12NGC-COMPSAM_V6C.tar.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/dr12_multidark_patchy_mocks/Patchy-Mocks-Randoms-DR12SGC-COMPSAM_V6C_x50.tar.gz -P path/to/folder/
# wget -N https://data.sdss.org/sas/dr12/boss/lss/dr12_multidark_patchy_mocks/Patchy-Mocks-Randoms-DR12NGC-COMPSAM_V6C_x50.tar.gz -P path/to/folder/

def read_sim_data(filename):
    ''' Read the random simulation catalogues from CSV '''
    sim_cat = nbodykit.lab.CSVCatalog(os.path.join(base_dir, filename),\
        names=['RA', 'DEC', 'Z', 'dummy1', 'NZ', 'dummy2', 'veto', 'Weight'])
    print('sim_cat.columns = ', sim_cat.columns)
    sim_cat = sim_cat[(sim_cat['Z'] > zmin) & (sim_cat['Z'] < zmax)]
    sim_cat = sim_cat[(sim_cat['veto'] > 0)]
    sim_cat['WEIGHT_FKP'] = 1./(1. + 10000.*sim_cat['NZ']);
    return sim_cat

def read_sim_ran(filename):
    ''' Read the simulation catalogues from CSV '''
    ran_cat = nbodykit.lab.CSVCatalog(os.path.join(base_dir, filename),\
        names=['RA', 'DEC', 'Z', 'NZ', 'dummy1', 'veto', 'Weight'])
    print('ran_cat.columns = ', ran_cat.columns)
    ran_cat = ran_cat[(ran_cat['Z'] > zmin) & (ran_cat['Z'] < zmax)]
    ran_cat = ran_cat[(ran_cat['veto'] > 0)]
    ran_cat['WEIGHT_FKP'] = 1./(1. + 10000.*ran_cat['NZ']);
    return ran_cat

def read_data(filename):
    ''' Read the data catalogues from FITS '''
    galaxies_cat = nbodykit.lab.FITSCatalog(os.path.join(base_dir, filename))
    print('galaxies_cat.columns = ', galaxies_cat.columns)
    galaxies_cat = galaxies_cat[(galaxies_cat['Z'] > zmin) & (galaxies_cat['Z'] < zmax)]
    galaxies_cat['Weight'] = galaxies_cat['WEIGHT_SYSTOT'] * (galaxies_cat['WEIGHT_NOZ'] + galaxies_cat['WEIGHT_CP'] - 1.0)
    #zmean = np.average(galaxies_cat['Z'].compute(), weights=(galaxies_cat['Weight']*galaxies_cat['WEIGHT_FKP']).compute())
    return galaxies_cat

def read_ran(filename):
    ''' Read the random catalogues from FITS '''
    randoms_cat = nbodykit.lab.FITSCatalog(os.path.join(base_dir, filename))
    print('randoms_cat.columns = ', randoms_cat.columns)
    randoms_cat = randoms_cat[(randoms_cat['Z'] > zmin) & (randoms_cat['Z'] < zmax)]
    return randoms_cat

def get_smooth_model(parameters, x, templates):
    ''' Combine a noBAO model with polynomials and linear bias '''
    polynomials = parameters[1]/x**3 + parameters[2]/x**2 + parameters[3]/x + parameters[4] + parameters[5]*x
    return parameters[0]*parameters[0]*templates['noBAO'](x) + polynomials

def get_shifted_model(parameters, x, templates):
    ''' Calculate a model including a shift given by alpha '''
    model = get_smooth_model(parameters, x, templates)
    return model*(1. + (templates['os_model'](x/parameters[6]) - 1.)*np.exp(-0.5*x**2*parameters[7]**2))

def within_priors(parameters):
    ''' Test for priors '''
    if len(parameters) > 6. and abs(parameters[6] - 1.) > 0.2:
        return False
    elif len(parameters) > 6. and (parameters[7] < 0. or parameters[7] > 20.):
        return False
    else:
        return True

def calc_chi2(parameters, data, templates, func):
    ''' Compares the model with the data '''
    if within_priors(parameters):
        chi2 = 0.
        # Loop over all datasets which are fit together
        for dataset in data:
            model = func(parameters, dataset['k'], templates)
            diff = (model - dataset['pk'])
            chi2 += np.dot(diff,np.dot(dataset['cov_inv'],diff))
        return chi2
    else:
        return 100000.

def get_loglike(parameters, data, templates, func):
    return -0.5*calc_chi2(parameters, data, templates, func)

def calc_pk(cosmo, data, random):
    ''' Calculate the power spectrum '''
    # add Cartesian position column
    data['Position'] = nbodykit.transform.SkyToCartesian(data['RA'],\
     data['DEC'], data['Z'], cosmo=cosmo)
    random['Position'] = nbodykit.transform.SkyToCartesian(random['RA'],\
     random['DEC'], random['Z'], cosmo=cosmo)

    # Combine data and random catalogue
    fkp = nbodykit.lab.FKPCatalog(data, random)
    # Assign point distribution to 3D grid
    mesh = fkp.to_mesh(Nmesh=512, nbar='NZ', comp_weight='Weight', fkp_weight='WEIGHT_FKP') # consider window='tsc', interlaced=True
    # Calculate power spectrum (monopole only)
    r = nbodykit.lab.ConvolvedFFTPower(mesh, poles=[0], dk=0.01, kmin=kmin)
    return r.poles

def process_data(cosmo, tag):
    # Read the BOSS data
    filename = 'galaxy_DR12v5_CMASSLOWZTOT_%s.fits' % tag
    galaxies = read_data(filename)

    # Read the random catalogues for calibration
    filename = 'random0_DR12v5_CMASSLOWZTOT_%s.fits' % tag
    randoms = read_ran(filename)
    return calc_pk(cosmo, galaxies, randoms)

def cmp_pk_with_error(pk1, pk2):
    ''' Compare two power spectra including uncertainties'''
    plt.clf()
    plt.errorbar(pk1['k'], pk1['k']*pk1['pk'], yerr=pk1['k']*np.sqrt(np.diagonal(pk1['cov'])),\
        marker='.', linestyle = 'None', label=pk1['label'])
    plt.errorbar(pk2['k'], pk2['k']*pk2['pk'], yerr=pk2['k']*np.sqrt(np.diagonal(pk2['cov'])),\
        marker='.', linestyle = 'None', label=pk2['label'])
    plt.legend(loc=0)
    plt.xlabel("k [$h$Mpc$^{-1}$]")
    plt.ylabel("kP [$h^{-2}$ Mpc$^2$]")
    plt.xlim(kmin, kmax)
    plt.show()
    return

def cmp_ratio_with_error(pk1, pk2, models=[]):
    ''' Compare two power spectra including uncertainties'''
    plt.clf()
    for model in models:
        plt.plot(model['k'], model['pk'])
    plt.errorbar(pk1['k'], pk1['ratio'], yerr=pk1['ratio_err'], marker='.',\
     linestyle = 'None', label=pk1['label'])
    plt.errorbar(pk2['k'], pk2['ratio'], yerr=pk2['ratio_err'], marker='.',\
     linestyle = 'None', label=pk2['label'])
    plt.axhline(y=1., color='black', linestyle='--')
    plt.legend(loc=0)
    plt.xlabel("k [$h$Mpc$^{-1}$]")
    plt.ylabel("$P(k)/P^{noBAO}(k)$")
    plt.xlim(kmin, kmax)
    plt.show()
    return

def calc_cov(tag, N):
    ''' Read simulation power spectra and return covariance matrix '''
    list_of_pks = []
    for i in range(1, N):
        pk_file = output_path + "/sims/pk_%s_%d.pickle" % (tag, i)
        pk = pickle.load( open( pk_file, "rb" ) )
        P = pk['power_0'].real - pk.attrs['shotnoise']
        # Limit the k range
        P = P[(pk['k'] < kmax)]
        list_of_pks.append(P)
    return np.cov(np.vstack(list_of_pks).T)

def process_sims(cosmo, tag, N):
    ''' Calculate the covariance matrix using the simulated BOSS datasets '''
    # Test whether we have already all power spectra and can directly return the cov
    try:
        return calc_cov(tag, N)
    except:
        pass
    # Read the random catalogue which can be paired with all of the simulated catalogues
    filename = 'Patchy-Mocks-Randoms-DR12%s-COMPSAM_V6C_x50.dat' % tag
    random = read_sim_ran(filename)

    for i in range(1, N):
        pk_file = output_path + "/sims/pk_%s_%d.pickle" % (tag, i)
        print('calculating %s... ' % pk_file)
        # Only calculate power spectra which we don't have on disk
        if not os.path.isfile(pk_file):
            filename = 'Patchy-Mocks-DR12%s-COMPSAM_V6C/Patchy-Mocks-DR12%s-COMPSAM_V6C_%0.4d.dat' % (tag, tag, i)
            sim = read_sim_data(filename)
            pk = calc_pk(cosmo, sim, random)
            # store the power spectra
            pickle.dump( pk, open( pk_file, "wb" ) )
    return calc_cov(tag, N)

def process_sims_with_MPI(cosmo, tag, N):
    ''' Calculate the covariance matrix using the simulated BOSS datasets '''
    # Test whether we have already all power spectra and can directly return the cov
    try:
        return calc_cov(tag, N)
    except:
        pass
    # this splits the communicator into chunks of size of roughly N
    with nbodykit.lab.TaskManager(1, use_all_cpus=True) as tm:
        # Read the random catalogue which can be paired with all of the simulated catalogues
        filename = 'Patchy-Mocks-Randoms-DR12%s-COMPSAM_V6C_x50.dat' % tag
        random = read_sim_ran(filename)
        # loop over each box number in parallel
        for i in tm.iterate(range(1, N)):
            pk_file = output_path + "/sims/pk_%s_%d.pickle" % (tag, i)
            print('calculating %s... ' % pk_file)
            # Only calculate power spectra which we don't have on disk
            if not os.path.isfile(pk_file):
                filename = 'Patchy-Mocks-DR12%s-COMPSAM_V6C/Patchy-Mocks-DR12%s-COMPSAM_V6C_%0.4d.dat' % (tag, tag, i)
                sim = read_sim_data(filename)
                pk = calc_pk(cosmo, sim, random)
                # store the power spectra
                pickle.dump( pk, open( pk_file, "wb" ) )
    return calc_cov(tag, N)

def get_oscillation(krange, Pk_class, Pk_without_BAO):
    ''' Get an oscillation only power spectrum '''
    cov_inv = np.identity(len(krange))
    start = [1., 0., 0., 0., 0., 0.]
    result = op.minimize(calc_chi2, start, args=( [{ 'k': krange, 'pk': Pk_class(krange), 'cov_inv': cov_inv }],\
     { 'noBAO': Pk_without_BAO }, get_smooth_model))
    yolin = Pk_class(krange)/get_smooth_model(result["x"], krange, { 'noBAO': Pk_without_BAO })
    return interp.interp1d(krange, yolin)

def get_percentiles(chain, labels):
    ''' Calculate constraints and uncertainties from MCMC chain '''
    per = np.percentile(chain, [50., 15.86555, 84.13445, 2.2775, 97.7225], axis=0)
    per = np.array([per[0], per[0]-per[1], per[2]-per[0], per[0]-per[3], per[4]-per[0]])
    for i in range(0, len(per[0])):
        print("%s = %f +%f -%f +%f -%f" % (labels[i], per[0][i], per[1][i], per[2][i], per[3][i], per[4][i]))
    return per

def inspect_chain(list_of_samplers, labels=[]):
    ''' Print chain properties '''
    Nchains = len(list_of_samplers)
    dim = list_of_samplers[0].chain.shape[2]
    if not labels:
        # set default labels
        labels = [('para_%i' % i) for i in range(0,dim)]

    mergedsamples = []
    for jj in range(0, Nchains):
        chain_length = list_of_samplers[jj].chain.shape[1]
        mergedsamples.extend(list_of_samplers[jj].chain[:, int(chain_length/2):, :].reshape((-1, dim)))

    # write out chain
    res = open("%schain.dat" % output_path, "w")
    for row in mergedsamples:
        for el in row:
            res.write("%f " % el)
        res.write("\n")
    res.close()

    print("length of merged chain = ", len(mergedsamples))
    try:
        for jj in range(0, Nchains):
            print("Mean acceptance fraction for chain ", jj,": ", np.mean(list_of_samplers[jj].acceptance_fraction))
    except Exception as e:
        print("WARNING: %s" % str(e))
    try:
        for jj in range(0, Nchains):
            print("Autocorrelation time for chain ", jj,": ", list_of_samplers[jj].get_autocorr_time())
    except Exception as e:
        print("WARNING: %s" % str(e))

    try:
        fig = corner.corner(mergedsamples, quantiles=[0.16, 0.5, 0.84], plot_density=False,\
            show_titles=True, title_fmt=".3f", labels=labels)
        fig.savefig("%scorner.png" % output_path)
    except Exception as e:
        print("WARNING: %s" % str(e))

    fig, axes = plt.subplots(dim, 1, sharex=True, figsize=(8, 9))
    for i in range(0, dim):
        for jj in range(0, Nchains):
            axes[i].plot(list_of_samplers[jj].chain[:, :, i].T, alpha=0.4)
        #axes[i].yaxis.set_major_locator(MaxNLocator(5))
        axes[i].set_ylabel(labels[i])
    fig.tight_layout(h_pad=0.0)
    fig.savefig("%stime_series.png" % output_path)

    try:
        return get_percentiles(mergedsamples, labels)
    except Exception as e:
        print("WARNING: %s" % str(e))
        return None

def gelman_rubin_convergence(within_chain_var, mean_chain, chain_length):
    ''' Calculate Gelman & Rubin diagnostic
    # 1. Remove the first half of the current chains
    # 2. Calculate the within chain and between chain variances
    # 3. estimate your variance from the within chain and between chain variance
    # 4. Calculate the potential scale reduction parameter '''
    Nchains = within_chain_var.shape[0]
    dim = within_chain_var.shape[1]
    meanall = np.mean(mean_chain, axis=0)
    W = np.mean(within_chain_var, axis=0)
    B = np.arange(dim,dtype=np.float)
    B.fill(0)
    for jj in range(0, Nchains):
        B = B + chain_length*(meanall - mean_chain[jj])**2/(Nchains-1.)
    estvar = (1. - 1./chain_length)*W + B/chain_length
    return np.sqrt(estvar/W)

def prep_gelman_rubin(sampler):
    dim = sampler.chain.shape[2]
    chain_length = sampler.chain.shape[1]
    chainsamples = sampler.chain[:, int(chain_length/2):, :].reshape((-1, dim))
    within_chain_var = np.var(chainsamples, axis=0)
    mean_chain = np.mean(chainsamples, axis=0)
    return within_chain_var, mean_chain

def runMCMC(start, data, templates):
    ''' Perform MCMC '''
    dim = len(start)
    Nchains = 4
    nwalkers = 20
    ichaincheck = 400
    minlength = 2000
    epsilon = 0.04

    labels = ['b', 'A1', 'A2', 'A3', 'A4', 'A5', 'alpha', 'sigmaNL']
    expected_error = [0.1, 1., 1., 1., 1., 1., 0.05, 0.1]
    # Set up the sampler.
    pos=[]
    list_of_samplers=[]
    for jj in range(0, Nchains):
        pos.append([start + (2.*np.random.random_sample((dim,)) - 1.)*expected_error for i in range(nwalkers)])
        list_of_samplers.append(emcee.EnsembleSampler(nwalkers=nwalkers, dim=dim, lnpostfn=get_loglike,\
         args=(data, templates, get_shifted_model)))

    # Start MCMC
    print("Running MCMC... ")
    within_chain_var = np.zeros((Nchains, dim))
    mean_chain = np.zeros((Nchains, dim))
    scalereduction = np.arange(dim, dtype=np.float)
    scalereduction.fill(2.)

    itercounter = 0
    chainstep = minlength
    while any(abs(1. - scalereduction) > epsilon):
        itercounter += chainstep
        for jj in range(0, Nchains):
            for result in list_of_samplers[jj].sample(pos[jj], iterations=chainstep, storechain=True):
                pos[jj] = result[0]
            # we do the convergence test on the second half of the current chain (itercounter/2)
            within_chain_var[jj], mean_chain[jj] = prep_gelman_rubin(list_of_samplers[jj])
        scalereduction = gelman_rubin_convergence(within_chain_var, mean_chain, int(itercounter/2))
        print("scalereduction = ", scalereduction)
        chainstep = ichaincheck
    # Investigate the chain and print out some metrics
    return inspect_chain(list_of_samplers, labels)

def main():
    cosmo = nbodykit.lab.cosmology.Cosmology(Omega0_cdm=0.26185743, Omega0_b=0.04814257, h=0.676)

    # Get the data power spectra
    pk_file = output_path + "/pk_South.pickle"
    if not os.path.isfile(pk_file):
        pk_south = process_data(cosmo, 'South')
        pickle.dump( pk_south, open( pk_file, "wb" ) )
    else:
        pk_south = pickle.load( open( pk_file, "rb" ) )
    pk_file = output_path + "/pk_North.pickle"
    if not os.path.isfile(pk_file):
        pk_north = process_data(cosmo, 'North')
        pickle.dump( pk_north, open( pk_file, "wb" ) )
    else:
        pk_north = pickle.load( open( pk_file, "rb" ) )
    P_south = pk_south['power_0'].real - pk_south.attrs['shotnoise']
    P_north = pk_north['power_0'].real - pk_north.attrs['shotnoise']

    # Get the covariance metrices
    cov_south = process_sims(cosmo, 'SGC', 1000)
    cov_south_inv = inv(cov_south)
    cov_north = process_sims(cosmo, 'NGC', 1000)
    cov_north_inv = inv(cov_north)

    # Store power spectrum details in a dictionary
    boss_data = [{ 'k': pk_south['k'][(pk_south['k'] < kmax)], 'pk': P_south[(pk_south['k'] < kmax)],\
     'label': 'BOSS south', 'cov': cov_south, 'cov_inv': cov_south_inv},\
     { 'k': pk_north['k'][(pk_north['k'] < kmax)], 'pk': P_north[(pk_north['k'] < kmax)],\
     'label': 'BOSS north', 'cov': cov_north, 'cov_inv': cov_north_inv }]
    #cmp_pk_with_error(pk_plot1, pk_plot2)

    # Isolate the BAO signal
    Pk_without_BAO = nbodykit.lab.cosmology.power.linear.LinearPower(cosmo, redshift=0, transfer='NoWiggleEisensteinHu')
    Pk_class = nbodykit.lab.cosmology.power.linear.LinearPower(cosmo, redshift=0, transfer='CLASS')
    krange = np.arange(0.001, 0.5, 0.001)
    os_model = get_oscillation(krange, Pk_class, Pk_without_BAO)
    start = [2.37, -0.076, 38., -3547., 15760., -22622., 1., 9.41]
    result = op.minimize(calc_chi2, start, args=(boss_data, { 'noBAO': Pk_without_BAO, 'os_model': os_model },\
     get_shifted_model))
    print("result['x'] = ", result['x'])

    krange = np.arange(kmin, kmax, 0.001)
    best_fit_model = get_shifted_model(result["x"], krange, { 'noBAO': Pk_without_BAO, 'os_model': os_model })/get_smooth_model(result["x"], krange, { 'noBAO': Pk_without_BAO })
    boss_data[0]['ratio'] = boss_data[0]['pk']/get_smooth_model(result["x"], boss_data[0]['k'], { 'noBAO': Pk_without_BAO })
    boss_data[0]['ratio_err'] = np.sqrt(np.diagonal(boss_data[0]['cov']))/get_smooth_model(result["x"], boss_data[0]['k'], { 'noBAO': Pk_without_BAO })
    boss_data[1]['ratio'] = boss_data[1]['pk']/get_smooth_model(result["x"], boss_data[1]['k'], { 'noBAO': Pk_without_BAO })
    boss_data[1]['ratio_err'] = np.sqrt(np.diagonal(boss_data[1]['cov']))/get_smooth_model(result["x"], boss_data[1]['k'], { 'noBAO': Pk_without_BAO })
    cmp_ratio_with_error(boss_data[0], boss_data[1], [{ 'k': krange, 'pk': best_fit_model }])

    per = runMCMC(result["x"], boss_data, { 'noBAO': Pk_without_BAO, 'os_model': os_model })
    if per is not None:
        Hz = cosmo.efunc(zmean)*cosmo.H0*cosmo.h
        DC = (1.+zmean)*cosmo.angular_diameter_distance(zmean)/cosmo.h
        c_km_s = (speed_of_light/1000.)
        DVfid = ( DC**2*(zmean*c_km_s/Hz) )**(1./3.)
        DV = per[:,6]*DVfid
        print("DV = %f +%f -%f +%f -%f\n" % (DV[0], DV[1], DV[2], DV[3], DV[4]))
    else:
        print("Problem with MCMC chain")
    return

if __name__ == '__main__':
    main()


"""
Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.

An example is the root-to-leaf path 1->2->3 which represents the number 123.

Find the total sum of all root-to-leaf numbers.

For example,

    1
   / \
  2   3
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.

Return the sum = 12 + 13 = 25.
"""
__author__ = 'Danyang'
# Definition for a  binary tree node
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def sumNumbers(self, root):
        """
        :param root: TreeNode
        :return: Integer
        """
        result = []
        self.dfs(root, "", result)
        result = [int(element) for element in result]
        return sum(result)

    def dfs(self, root, cur, result):
        """
        dfs, using string as cur (kind of collector).
        :param root: TreeNode
        :param cur: str
        :param result: list
        :return: None
        """
        if not root:
            return
        cur = cur+str(root.val)
        if not root.left and not root.right:
            result.append(cur)
            return

        if root.left:
            self.dfs(root.left, cur, result)
        if root.right:
            self.dfs(root.right, cur, result)

    def dfs_error(self, root, cur, result):
        """
        Using list as cur has some reference issues
        :param root: TreeNode
        :param cur: list
        :param result: list
        :return: None
        """
        if not root:
            return

        cur.append(root.val)

        if not root.left and not root.right:
            result.append(cur)
            return

        if root.left:
            self.dfs_error(root.left, cur, result)  # reference to the same list
        if root.right:
            self.dfs_error(root.right, cur, result)

if __name__=="__main__":
    nodes = [TreeNode(0), TreeNode(1), TreeNode(3)]
    nodes[0].left = nodes[1]
    nodes[0].right = nodes[2]
    Solution().sumNumbers(nodes[0])


from datetime import datetime
#navidad = datetime.strptime("2021-12-25", "%Y-%m-%d")
#fin_anio = datetime.strptime("2021-12-31", "%Y-%m-%d")
#diferencia = fin_anio-navidad
#print(f"La diferencia es de {diferencia.days} días y {diferencia.m} segundos. La diferencia total es de {diferencia.total_seconds()} segundos")

def calcular_edad(dia_nacio:int, mes_nacio:int, anio_nacio:int, dia_actual:int, mes_actual:int, anio_actual:int):
    fechaNacimiento = str(anio_nacio) + ("-") + str(mes_nacio) + ("-") + str(dia_nacio)
    fechaActual = str(anio_actual) + ("-") + str(mes_actual) + ("-") + str(dia_actual)
    fechaNacimiento = datetime.strptime(fechaNacimiento, "%Y-%m-%d")
    fechaActual = datetime.strptime(fechaActual, "%Y-%m-%d")
    diferencia = fechaActual - fechaNacimiento
    dias = diferencia.days
    while dias != 0:
        anios,meses,diasR = 0,0,0
        if dias > 365 and dias // 365 >= 1:
            anios = dias // 365
            dias -= ( anios * 365)
            anios = str(anios)
        elif dias < 365 and dias // 30 >= 1:
            meses = dias / 30
            dias -= (meses * 30)
            meses = str(meses)
        else:
            diasR = dias
            dias -= diasR
            diasR = str(diasR)

    print(f"{anios}, {meses}, {diasR}")

calcular_edad(20,11,1986,16,10,1987)


# -*- coding: utf-8 -*-
"""
@author: cqx931
2020
"""
import os
import json
import pygame
from pygame import ftfont
import pickle
import operator
import csv
import tsv
import re
import sys

from fontTools.ttLib import TTFont

# based on the csv, all characters and its id
char2id = {}
id2char = {}
# a dictionary that contains all the categories and its characters
cat = {}
# a compact dictionary that contains all characters
dic_c = {}

# all classes
class2id = {}
id2class = {}
# final data format for training
data = {}

SIZE = 32
LIMIT = 11
debug = True

COMP  = ["⿱","⿳", "⿰","⿲", "⿻","⿸","⿹", "⿺", "⿷", "⿴", "⿵","⿶"]

def draw(c, idx, directory):
    # print(c,directory)
    pygame.init()
    font_t = ftfont.Font("fonts/NotoSansCJKtc-Regular.otf", 32)
    font_s = ftfont.Font("fonts/NotoSansCJKsc-Regular.otf", 32)
    rtext = font_t.render(c, True, (0, 0, 0), (255, 255, 255))
    if rtext is None:
        rtext = font_s.render(c, True, (0, 0, 0), (255, 255, 255))

    rtext = pygame.transform.scale(rtext, (32, 32))

    pygame.image.save(rtext, directory + str(idx) + ".jpg")
    return True

def drawImages():
    pygame.init()
    dict = open("data/VC/dict.csv", "r")
    c_image = 0
    missing = []

    if not os.path.exists('data/VC/img_all/'):
        os.makedirs('data/VC/img_all/')
    print("Generating Images:")
    for idx,entry in enumerate(dict):
        items = entry.split(',')
        valid = draw(items[0], idx, "data/VC/img_all/")
        if valid:
            c_image +=1
        else:
            missing.append(idx)
        if idx%500 == 0:
            print(idx)
    print("Total images generated:", c_image)
    print("Missing:", len(missing))

# create a clear csv file from the original dict file
def generateCSV():
    raw_dict = open("data/VC/dict.csv", "r")
    with open('data/VC/new_dict.csv', 'w') as csvfile:
        fieldnames = ['character', 'decomposition','radical']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        # writer.writeheader()
        for idx,entry in enumerate(raw_dict):
            entry = entry.split(',')
            if len(entry) <= 3:
                # from csv
                c = entry[0]
                d = entry[1]
                r = entry[2]
            else:
                # from raw json data
                entry = json.loads(entry)
                c = entry["character"]
                d = entry["decomposition"]
                r = entry["radical"]

            if (c == r): d = 'X'

            if len(d) == 4:
                if '⿳' in d:
                    d = '⿱' + d[1] + d[3]
                elif '⿲' in d:
                    d = '⿰' + d[1] + d[3]
            elif len(d) > 4 and d[1] not in COMP:
                d = d[0] + d[1] + "?"
            elif len(d) > 4:
                d = d[0] + "?" + d[len(d)-1]
            writer.writerow({'character': c, 'decomposition': d, 'radical':r})

def simplifyDecomposition(d):
    ###### deal with traditional/simplified classes
    d = d.replace("鸟", "鳥")
    d = d.replace("马", "馬")
    d = d.replace("纟", "糹")
    d = d.replace("车", "車")
    d = d.replace("饣", "飠")
    d = d.replace("贝", "貝")
    d = d.replace("见", "見")
    d = d.replace("讠", "言")
    d = d.replace("钅", "釒")
    d = d.replace("车", "車")
    d = d.replace("门", "門")
    d = d.replace("风", "風")
    d = d.replace("白", "日")
    d = d.replace("区", "區")
    d = d.replace("页", "頁")
    d = d.replace("鱼", "魚")
    d = d.replace("齿", "齒")
    d = d.replace("韦", "韋")
    d = d.replace("麦", "麥")
    d = d.replace("尧", "堯")
    d = d.replace("爱", "愛")
    d = d.replace("仑", "侖")
    d = d.replace("仓", "倉")
    d = d.replace("乔", "喬")
    d = d.replace("区", "區")
    d = d.replace("夹", "夾")
    d = d.replace("会", "會")
    d = d.replace("争", "爭")
    # d = d.replace("韦", "韋")
    # 鹵
    ######
    # merge visually similar cases
    d = d.replace("鬥", "門")
    d = d.replace("⿸广", "⿸厂")
    d = d.replace("士", "土")
    d = d.replace("犬", "大")
    d = d.replace("良", "艮")
    d = d.replace("凡", "几")
    d = d.replace("氺", "水")
    d = d.replace("巴", "己")
    d = d.replace("巳", "己")
    d = d.replace("色", "己")
    d = d.replace("每", "母")
    d = d.replace("⿰手", "⿰扌")
    d = d.replace("島", "鳥")
    d = d.replace("烏", "鳥")
    d = d.replace("且", "目")
    d = d.replace("瓜", "爪")
    d = d.replace("令", "今")
    d = d.replace("老", "耂")
    d = d.replace("聿", "肀")
    d = d.replace("虎", "虍")
    d = d.replace("夬", "夫")
    d = d.replace("夬", "夫")
    d = d.replace("矢", "夫")
    d = d.replace("失", "夫")
    d = d.replace("必", "心")
    d = d.replace("龺", "𠦝")
    d = d.replace("⺼", "月")
    d = d.replace("攵","夂")
    d = d.replace("𠂊", "⺈")
    # d = d.replace("爰", "愛")
    d = d.replace("主", "王")
    return d

def getMostCommonCh():
    mostCommonChs = ''
    basic_dict = open("data/VC/mostCommon.txt", "r")
    for line in basic_dict:
        mostCommonChs = line
    return mostCommonChs.replace("\n","")

def getRefCSV():
    ref = {}
    with open('data/VC/dict.csv', mode='r') as infile:
        reader = csv.reader(infile)
        for rows in reader:
            ref[rows[0]] = rows[1]
    return ref

def addToDict(item, dict, value):
    if item not in dict:
        dict[item] = [value]
    else:
        dict[item].append(value)

def getPatterns(c,d):
    patterns = []
    # unclassifiable common ch
    if c in "幻巧枣垂甚临既释就疏聚舞疑敲黎氓衅棘甥粤赫兢孵幾虧歸叢務發執鞏喬壯農報縣畝棗隸參斃競營釋賴疊釁粵噩鹵":
        patterns.append("口")

    if len(d) == 3 and "X" not in d:
        d = simplifyDecomposition(d);

        pattern1 = d[0]+ d[1] +"X"
        pattern2 = d[0]+ "X" + d[2]

        if d[0] == "⿱" and d[1] in "山木口土日頁":
            pattern1 = d[0]+ d[1]
        if d[0] == "⿱" and d[2] in "山木口土日頁":
            pattern2 = d[0]+ d[2]

        if d[0] == "⿰" and d[1] in "舌月風阝日土":
            pattern1 = d[0]+ d[1]
        if d[0] == "⿰" and d[2] in "舌月風阝日土":
            pattern2 = d[0]+ d[2]

        if "⿻行" in d:
            pattern1 = "⿰彳X"

        radicals = "耂肀虍光予军用多食耳正臣谷缶彐是豆林爪瓦鹿音角云共旦羽豕果里止厶見毛母比鬼青辛非文韋立子十弓方又牛今車魚夫羊巾田酉隹米禾|馬足貝山石王目火女鳥虫言父儿麥尚艹咸直"
        # The following candidates are not suitable for merging: 木口几

        # Classify the following as component, ignore composition
        if d[1] in radicals and radicals.index(d[1]) > -1:
            current = radicals[radicals.index(d[1])]
            if d[1] == "车":
                pattern1 = ("X車")
            elif d[1] == "鱼":
                pattern1 = ("X魚")
            else:
                pattern1 = ("X" + current)

        if d[2] in radicals and radicals.index(d[2]) > -1:
            current = radicals[radicals.index(d[2])]
            if d[2] == "车":
                pattern2 = ("X車")
            elif d[2] == "鱼":
                pattern2 = ("X魚")
            else:
                pattern2 = ("X" + current)

        if d[1] in "冖宀穴":
           pattern1 = "⿱冖X"

        if pattern2 == "⿰X戈":
            pattern2 = "⿰X弋"

        patterns.append(pattern1)
        patterns.append(pattern2)
    elif len(d) < 3 and "X" not in d:
        if "品" in d:
            patterns.append("品X")
        else:
            # 独体字
            patterns.append("口")
    else:
        patterns.append("X")
    return patterns

def addAllChToDict(all):
    for idx, ch in all :
        if idx not in dic_c:
            dic_c[idx] = ch

def addMissingCommonCh():
    allCh = "".join(dic_c.values())
    mostCommonChs = getMostCommonCh();
    ref_dict = getRefCSV()
    tobeAdded = 0
    for c in list(mostCommonChs):
        if c not in allCh:
            solved = False
            d = ref_dict[c]
            idx = char2id[c]
            patterns = getPatterns(c, d)
            if "口" in patterns or "X" in patterns:
                addToDict("口", cat, (idx,c))
                dic_c[idx] = c
            else:
                for p in patterns:
                    if p in cat:
                        cat[p].append((idx,c))
                        solved = True
                        # break patterns loop
                        break
                if not solved:
                    tobeAdded += 1
                    print(c, d, patterns)
    print("Still need to add:", tobeAdded)

def renderData():
    for c_i,c in enumerate(cat):
        class2id[c] = c_i
        id2class[c_i] = c
        # os.makedirs("img/"+ c)
        for idx, char in cat[c]:
            if idx not in data:
                data[char2id[char]] = (str(c_i),char,c)
            else:
                x, y, z = data[char2id[char]]
                data[char2id[char]] = (x + " " + str(c_i), char, z + " " + c)

def postProcess():
    count = 0
    csv = open('data/VC/dict.csv', "r")
    for idx,entry in enumerate(csv):
        count += 1
        entry = entry.split(',')
        c = entry[0]
        d = entry[1]
        id2char[idx] = c
        char2id[c] = idx
        patterns = getPatterns(c, d)
        for p in patterns:
            addToDict(p, cat, (idx,c))
        # End of for loop

    print("All Categories:",len(cat))

    # clearn cat{} & render dict_c
    for r in list(cat.keys()):
        if "?" not in r and "？" not in r and len(cat[r]) > LIMIT and r is not "X" and r not in ["⿰镸X","⿱X廾","⿱殸"]:
            # remove too detailed categories
            addAllChToDict(cat[r])
        elif r in ["X父","⿺廴X","⿰𠦝X","⿱吅X","X麥","X正","X光","⿱X天","⿱X蟲","X直"] :
            # non frequent but common components
            addAllChToDict(cat[r])
        elif r in ["⿰X反","X尚","⿱X八","X用","⿱覀X","⿱X示","⿱臼X","⿱X夂","⿱X小","⿸户X","X彐","⿰X卜","⿱X寸","⿱丷X","⿱龹X","⿶凵X","⿵几X", "⿵冂X","⿰赤X","X予","X多","X食","⿱X几","X臣","X咸","⿰X佥"]:
            addAllChToDict(cat[r])
        else:
            del cat[r]
            # if debug:
            #     print("Delete category:", r)

    addMissingCommonCh()
    renderData()

    # print all final labels
    if debug:
        for w in sorted(cat, key=lambda k: len(cat[k]), reverse=True):
            print(w, len(cat[w]))

    print("Final Categories:", len(cat))
    # # print(dic_c)
    print("Dictionary:",len(dic_c),"/", count)
    name = "v3.2"

    with open('data/VC/'+ name +'/charset_' + str(len(cat)) + '.txt', 'w') as outfile:
        allCh = "".join(dic_c.values())
        outfile.write(allCh)
        print("charset: save to file")

    with open('data/VC/' + name +'/' + name + "_" +  str(len(cat)) + 'C.json', 'w') as outfile:
        json.dump(data, outfile)
        print("json:save to file")

    # writer = tsv.TsvWriter(open("data/VC/"+ name +"/" + name +"_meta.tsv", "w"))
    # for idx, entry in enumerate(data):
    #     writer.line(str(idx) + "\t" + id2char[entry])
    # writer.close()
    # print("tsv:save to file")

def main():
    if len(sys.argv) > 1 and sys.argv[1] == "-img":
        generateCSV()
        drawImages()
        postProcess()
    else:
        generateCSV()
        postProcess()

if __name__ == '__main__':
    main()

# 9484 256C


#!/usr/bin/env python

import unittest
import sys
import json
sys.path.append("..")

import convert_image

class TestConvert(unittest.TestCase):

    def convert_image(self, image_name, width):
        """ Converts image
        """
        fl_original = f"./tests/images/{image_name}"
        fl_ascii = f"./tests/images/{image_name}.ascii"
        fl_meta = f"./tests/images/{image_name}.meta"

        convert_image.main(fl_original, fl_ascii, fl_meta, width=width)

    def check_meta(self, image_name, speed_min=0, speed_max=1):
        """ Checks meta
        """

        fl_ascii_test = f"./tests/images/test_{image_name}.meta"
        with open(fl_ascii_test) as fd:
            meta_test = json.load(fd)

        fl_ascii = f"./tests/images/{image_name}.meta"
        with open(fl_ascii) as fd:
            meta = json.load(fd)

        self.assertEqual(meta["state"], meta_test["state"])
        self.assertTrue(0 <= meta["convert_time"] <= speed_max)

    def compare_images(self, image_name):
        """ Compares images
        """

        fl_ascii_test = f"./tests/images/test_{image_name}.ascii"
        with open(fl_ascii_test) as fd:
            test_image = fd.read()

        fl_ascii = f"./tests/images/{image_name}.ascii"
        with open(fl_ascii) as fd:
            image = fd.read()

        self.assertEqual(image, test_image)

    def test_BW_convert(self):
        """ Tests BW image
        """

        image_name = "github.png"
        width = 100
        self.convert_image(image_name, width)
        self.compare_images(image_name)
        self.check_meta(image_name, speed_max=1)

    def test_RGB_convert(self):
        """ Tests RGB image
        """

        image_name = "planet.png"
        width = 100
        self.convert_image(image_name, width)
        self.compare_images(image_name)
        self.check_meta(image_name, speed_max=1)

    def test_failure(self):
        """ Tests failure
        """

        image_name = "missing.png"
        width = 100
        try:
            self.convert_image(image_name, width)
        except Exception:
            pass

        fl_ascii = f"./tests/images/{image_name}.meta"
        with open(fl_ascii) as fd:
            meta = json.load(fd)

        self.assertEqual(meta["state"], "error")
        self.assertEqual(meta["error"], "[Errno 2] No such file or directory: './tests/images/missing.png'")

if __name__ == '__main__':
    unittest.main()