from subprocess import run
import os
from os import mkdir
from shutil import copy, move
from argparse import Namespace

import pandas as pd
import numpy as np

import pickle
from copy import copy as cp

from skimage import measure
from scipy.ndimage import distance_transform_edt as edist

def read_permeability():
    """
    Reads the permeability value from the data file 'frac.txt'.

    The function retrieves the permeability value, which is expected to be found after the first
    equal sign ('=') and before the substring '\nName' on the fourth line from the end of the file.

    It uses the pandas library to read and handle the data file. If the permeability value cannot be
    converted to a float or if the file 'frac.txt' cannot be found, exceptions will be raised.

    Parameters
    -----------
        None

    Returns
    --------
        perm : float
            The permeability value parsed from 'frac.txt'.

    Notes
    ------
        None

    Raises
    -------
        ValueError
            If the permeability value is not a valid float.
        FileNotFoundError
            If 'frac.txt' cannot be found.
    """
    # Read the data file using pandas
    file = pd.read_csv('frac.txt', header=None)

    # Find the index of the first equal sign in the last fourth line
    ind1 = str(file.iloc[-4]).find('=')+2
    # Find the index of the substring '\nName' in the last fourth line
    ind2 = str(file.iloc[-4]).find('\nName')

    # Extract the permeability value from the string between the two indices and convert it to float
    perm = float(str(file.iloc[-4])[ind1:ind2])
    return perm

def check_lbm_install():
    """
    Checks whether the MPLBM software has been installed.

    The function verifies the presence of the 'permeability' file in the 'lbm_light/src/1-phase_LBM/' directory.
    If the file is not present, it indicates that the LBM software is not installed and raises a
    NotImplementedError. If the file is present, it prints a message to the console to indicate that the
    LBM installation was found.

    Parameters
    -----------
        None

    Returns
    --------
        None

    Raises
    -------
        NotImplementedError
            If the 'permeability' file cannot be found in the 'lbm_light/src/1-phase_LBM/' directory, suggesting that
            the LBM software has not been installed.

    Notes
    ------
        The function makes use of the 'os' library to interact with the operating system.

    Examples
    --------
        >>> check_lbm_install()
        LBM installation was found
    """
    if not os.path.isfile('lbm_light/src/1-phase_LBM/permeability'):
        raise NotImplementedError('\nLBM has not been installed\n')
    else:
        print('\nLBM installation was found\n')

def create_folder(folder_name):
    """
    Creates a new folder with a given name.

    The function attempts to create a new directory with the specified name. If a directory with the
    same name already exists, the function will not create a new directory and will not raise any errors.

    Parameters
    -----------
        folder_name : str
            The name of the folder to be created.

    Returns
    -------
        None

    Raises
    -------
        FileExistsError
            If a directory with the specified name already exists. This error is caught internally and
            does not interrupt the program execution.

    Notes
    ------
        The function uses the 'os' library function 'mkdir' to create the new directory.

    """
    try:
        mkdir(folder_name)
    except FileExistsError:
        pass

def replace_word(file, old_word, new_word):
    """
    Replaces occurrences of a word in a file with a new word.

    The function reads the contents of the specified file, replaces all occurrences of 'old_word'
    with 'new_word', and overwrites the file with the new contents. If the file does not exist or
    cannot be read, a FileNotFoundError will be raised. If the file cannot be written to, an
    OSError may be raised.

    Parameters
    ----------
        file : str
            The path of the file in which the word will be replaced.
        old_word : str
            The word to be replaced.
        new_word : str
            The word to replace 'old_word' with.

    Returns
    --------
        None

    Raises
    ------
        FileNotFoundError
            If the specified file does not exist or cannot be read.
        OSError
            If the file cannot be written to.

    Notes
    -----
        None

    """

    f = open(file,'r')
    filedata = f.read()
    f.close()

    newdata = filedata.replace(old_word, new_word)

    f = open(file,'w')
    f.write(newdata)
    f.close()

def create_single_phase_input_file(input_file_name, geom_name, domain_size, periodic, io_folders, sim_settings, save_vtks):
    """
    Creates an XML input file for a single phase flow simulation based on given parameters.

    The function uses the provided parameters to write the settings for a single phase flow simulation
    into an XML file. If a file with the specified name already exists, it will be overwritten.

    Parameters
    ----------
        input_file_name : str
            The name of the input file to create. If it already exists, it will be overwritten.
        geom_name : str
            The name of the .dat file that contains the entire geometry. Do not include the ".dat" extension.
        domain_size : list
            The size (in voxels) of the simulation domain, provided as a list of the form [x, y, z].
        periodic : list
            Determines whether the simulation should be periodic in the x, y, and z directions. This should be a list of three strings, either "True" or "False".
        io_folders : list
            A list containing the paths to the input and output folders, in that order.
        sim_settings : list
            A list containing the number of geometries/simulations, pressure, maximum number of iterations, and convergence, in that order.
        save_vtks : str
            Determines whether to save VTK files for the medium and velocity. This should be a string, either "True" or "False".

    Returns
    --------
        None

    Notes
    -----
        The function uses Python's built-in 'open' function to create and write to the XML file.

    Examples
    --------
        >>> create_single_phase_input_file('input.xml', 'geometry', [100, 100, 100], ['True', 'False', 'True'], ['input/', 'output/'], [5, 0.02, 2000, 0.0001], 'True')
        # This will create 'input.xml' with the specified settings.

    """
    # Parse geometry inputs
    x_size, y_size, z_size = domain_size
    periodic_x, periodic_y, periodic_z = periodic

    # Parse I/O inputs
    input_folder, output_folder = io_folders

    # Parse simulation inputs
    num_geoms_or_sims, pressure, max_iter, convergence = sim_settings

    # Open the input file, creating it if necessary
    with open(input_file_name, 'w') as file:
        file.write('<?xml version="1.0" ?>\n\n')  # Write XML header

        # Write the geometry settings to the file
        file.write('<geometry>\n')
        file.write(f'\t<file_geom> {geom_name} </file_geom>\n')
        file.write(f'\t<size> <x> {x_size} </x> <y> {y_size} </y> <z> {z_size} </z> </size>\n')
        file.write(f'\t<per> <x> {periodic_x} </x> <y> {periodic_y} </y> <z> {periodic_z} </z> </per>\n')
        file.write('</geometry>\n\n')

        # Write the I/O settings to the file
        file.write('<folder>\n')
        file.write(f'\t<in_f> {input_folder} </in_f>\n')
        file.write(f'\t<out_f> {output_folder} </out_f>\n')
        file.write('</folder>\n\n')

        # Write the simulation settings to the file
        file.write('<simulations>\n')
        file.write(f'\t<num> {num_geoms_or_sims} </num>\n')
        file.write(f'\t<press> {pressure} </press>\n')
        file.write(f'\t<iter> {max_iter} </iter>\n')
        file.write(f'\t<conv> {convergence} </conv>\n')
        file.write(f'\t<vtk_out> {save_vtks} </vtk_out>\n')
        file.write('</simulations>')

def create_geom_edist(rock, args):
    """
    Modifies a given rock matrix, calculates its Euclidean distance, creates a geometry file based on the
    modified matrix and returns it.

    This function primarily modifies an input rock matrix according to several configuration options provided
    in the 'args' parameter, calculates the Euclidean distance for the modified matrix, and writes it to a .dat
    file. Additionally, the function ensures that all the boundary conditions have bounding box nodes, and it
    pads the matrix if the 'num_slices' argument is provided.

    Parameters
    ----------
        rock : np.array
            The input rock matrix to be modified.
        args : argparse.Namespace
            An object containing various configuration options. This includes the 'swapXZ', 'scale_2', 'add_mesh',
            'num_slices', 'print_size' and 'name' flags, and 'loc' directory path.

    Returns
    -------
        erock : np.array
            The modified rock matrix after the Euclidean distance calculation.

    Raises
    ------
        NotImplementedError
            If the 'scale_2' or 'add_mesh' features are attempted to be used as they are not yet implemented.

    Notes
    -----
        The function uses numpy's 'transpose', 'pad' and 'tofile' functions, as well as a custom 'edist' function
        for Euclidean distance calculation.

    Examples
    --------
        >>> import numpy as np
        >>> import argparse
        >>> rock = np.array([[[0, 1, 0], [1, 0, 1], [0, 1, 0]], [[1, 0, 1], [0, 1, 0], [1, 0, 1]], [[0, 1, 0], [1, 0, 1], [0, 1, 0]]])
        >>> args = argparse.Namespace(swapXZ=False, scale_2=False, add_mesh=False, num_slices=2, print_size=True, name='geom', loc='./')
        >>> mod_rock = create_geom_edist(rock, args)
    """
    # If the 'swapXZ' flag is set, transpose the rock matrix accordingly
    if args.swapXZ:
        rock = rock.transpose([2, 1, 0])

    # If the 'scale_2' flag is set, raise an error as this feature is not yet implemented
    if args.scale_2:
        raise NotImplementedError('Feature not yet implemented')

    # Calculate the Euclidean distance for the rock matrix
    erock = edist(rock)

    # Ensure all the BCs have BB nodes
    erock[0,:,:] = erock[:,0,:] = erock[:,:,0] = 1
    erock[-1,:,:] = erock[:,-1,:] = erock[:,:,-1] = 1

    # Reopen the pores
    erock[rock==0] = 0

    # Get the final matrix with values [0,1,2]
    erock[(erock > 0) * (erock < 2)] = 1
    erock[erock > 1] = 2

    # If the 'add_mesh' flag is set, raise an error as this feature is not yet implemented
    if args.add_mesh:
        raise NotImplementedError('Feature not yet implemented')

    # If the 'num_slices' argument is provided, pad the 'erock' array accordingly
    if args.num_slices:
        erock = np.pad(erock, [(args.num_slices, args.num_slices), (0, 0), (0, 0)])

    # Determine the geometry name based on the 'print_size' flag
    if args.print_size:
        size = erock.shape
        geom_name = f'{args.name}_{size[0]}_{size[1]}_{size[2]}'
    else:
        geom_name = args.name

    # Modify the 'erock' array's data type and values for final output
    erock = erock.astype(np.int16)
    erock[erock==0] = 2608
    erock[erock==1] = 2609
    erock[erock==2] = 2610

    # Write the 'erock' array to a .dat file
    erock.flatten().tofile(f'{args.loc}/input/{geom_name}.dat')

    return erock

def erase_regions(rock):
    """
    Identifies and erases isolated regions within a given rock matrix.

    The function employs the `label` function from the skimage.measure module to detect connected components
    in the input rock matrix. Isolated regions are identified as those components which are not connected to
    the main body of the matrix (where the corresponding element in the labeled matrix is greater than 1).
    These regions are then removed (set to 0) from the rock matrix.

    Parameters
    ----------
        rock : np.array
            The input rock matrix to be processed.

    Returns
    -------
        rock : np.array
            The modified rock matrix with isolated regions removed.

    Notes
    -----
        The function uses skimage.measure's 'label' function for connected components detection. The background
        is set to 1, and connectivity is set to 1 to consider only orthogonally adjacent points.

    Examples
    --------
    >>> import numpy as np
    >>> rock = np.array([[[0, 1, 0], [1, 0, 1], [0, 1, 0]], [[1, 0, 1], [0, 1, 0], [1, 0, 1]], [[0, 1, 0], [1, 0, 1], [0, 1, 0]]])
    >>> mod_rock = erase_regions(rock)
    """
    # Use the measure.label function from the skimage.measure module to identify connected components in the rock matrix
    # Here, the background is set to 1, and connectivity is set to 1 to consider only orthogonally adjacent points
    blobs_labels = measure.label(rock, background=1, connectivity=1)

    # Erase all isolated regions within the rock matrix
    # This is done by setting all elements in 'rock' that are part of an isolated region (where the corresponding element in 'blobs_labels' is greater than 1) to 0
    rock[blobs_labels > 1] = 0

    return rock

class write_MPLBM():
    """
    This class encapsulates methods to set up, manage and execute an MPLBM (MultiPhase Lattice Boltzmann Method)
    simulation. It uses provided or default settings to create the necessary files and directories, write geometry,
    write inputs, and execute the simulation.
    """

    def __init__(self, frac_obj, buffer_layers=2, cpus=4, num_hrs=14, allocation=None):
        """
        Initializes the write_MPLBM class

        Parameters
        ----------
            frac_obj : object
                An object containing a fracture object.
            buffer_layers : int, optional
                The number of empty slices at the beginning and end of the domain for pressure boundary conditions.
                Defaults to 2.
            cpus : int, optional
                The number of CPUs to be used in the simulation. Defaults to 4.
            num_hrs : int, optional
                The number of hours the simulation should run. Defaults to 14.
            allocation : str, optional
                A specific allocation for the simulation. Defaults to None.

        Returns
        --------
            None

        Notes
        -----
            The class initializes by defining the source location of LBM (Lattice Boltzmann Method), creating required
            directories, copying the surface fraction object, and pre-processing the 3D fracture to generate an efficient
            geometry for simulation. It also writes a shell script to run the simulation.

            The class includes methods to create new folders, write geometry, write various input files, copy necessary
            data, edit inputs, and save the configuration as a pickle file for future reference.
        """

        # Location of LBM source file
        lbm_loc = '../../lbm_light/src/1-phase_LBM/permeability'

        # Define geometry namespace
        geom = Namespace()
        name = 'frac'
        geom.name = name
        geom.print_size = True
        geom.add_mesh = False
        geom.num_slices = buffer_layers
        geom.swapXZ = True
        geom.scale_2 = False

        # Create required directories
        create_folder('sims')
        self.folder_num = self.create_new_folder()
        self.folder_path = f'sims/frac_{self.folder_num}'
        create_folder(f'{self.folder_path}/input')

        # Assign folder path to geom.loc
        geom.loc = self.folder_path

        # Copy the surface fraction object and remove 'frac_3D' to reduce pickle size
        self.my_frac = cp(frac_obj)
        del self.my_frac.frac_3D

        self.buffer_layers = buffer_layers
        self.cpus = cpus
        self.num_hrs = num_hrs
        self.allocation = allocation

        # Preprocess the 3D fracture and generate an efficient geometry for simulation
        frac_3D = 1 - np.transpose(frac_obj.frac_3D, (2, 0, 1))
        frac_3D = erase_regions(frac_3D)
        frac_3D = create_geom_edist(frac_3D, geom)

        # Create input file
        frac_size = frac_3D.shape
        input_file_name = f"{self.folder_path}/{name}.xml"
        geom_name = f'{geom.name}_{frac_size[0]}_{frac_size[1]}_{frac_size[2]}'
        domain_size = [frac_size[0], frac_size[1], frac_size[2]]
        periodic = ["false", "false", "false"]

        create_folder(f'{self.folder_path}/output')

        io_folders = ['input/', 'output/']
        num_sims = 1
        sim_pressure = 0.0005
        sim_max_iter = 1000000
        sim_convergence = 0.0001
        sim_settings = [num_sims, sim_pressure, sim_max_iter, sim_convergence]
        save_vtks = "true"
        create_single_phase_input_file(input_file_name, geom_name, domain_size, periodic, io_folders, sim_settings, save_vtks)

        # Write shell script to run the simulation
        np.savetxt(f'{self.folder_path}/run_{name}.sh', [f'mpirun -n {cpus} {lbm_loc} {name}.xml > {name}.txt'], fmt='%s')

    def create_new_folder(self):
        """
        Creates a new simulation directory in the 'sims' folder. It searches through numbered folders
        'frac_i' (i ranging from 0 to 9999999999) and creates the first one that doesn't already exist.

        Parameters
        ----------
            None

        Returns
        -------
            i : int
                The number 'i' of the newly created folder.

        Notes
        ------
            None

        """

        for i in range(10000000000):
            try:
                mkdir(f'sims/frac_{i}')
                break
            except FileExistsError:
                continue
        return i

    def write_geom(self, frac):
        """
        Writes the 3D fracture geometry to a .dat file.

        The function first appends the shape of the input 'frac' array to the flattened 'frac' array,
        then saves this information to a .dat file in the current simulation's directory.

        Parameters
        ----------
            frac : np.array
                The 3D fracture geometry to be written.

        Returns
        -------
            None

        Notes
        ------
            None

        """
        tofile = np.append([*frac.shape], frac.flatten('F'))
        np.savetxt(f'{self.folder_path}/frac.dat', tofile, fmt='%.0f')

    def save_pickle(self):
        """
        Saves the current instance of the write_MPLBM class as a pickled object.

        Parameters
        -----------
            None

        Returns
        -------
            None

        Raises
        ------
            PicklingError
                If the pickling process encounters an object that it cannot serialize.

        Notes
        ------
            None
        """

        with open(f'{self.folder_path}/lbm_config.pk', 'wb') as f:
            pickle.dump(self, f, protocol = pickle.HIGHEST_PROTOCOL)


import cv2
import numpy as np
#
# cap= cv2.VideoCapture(1);
# print(cap.isOpened())
# while(cap.isOpened()):
#     ret,frame=cap.read()
#
#     print(cap.get(cv2.CAP_PROP_SETTINGS))
#
#     gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
#     cv2.imshow('Sushant',gray)
#
#     if cv2.waitKey(1) & 0xFF == ord('q'):
#         break
#
# cap.release()
# cv2.destroyAllWindows()

#
# img= cv2.imread('RGB_color.jpg',1)
#
#
# img=cv2.line(img,(0,0),(255,255),(255,0,0),5)
# img=cv2.rectangle(img,(193,324),(549,519),(0,0,0),5)
#
#
# cv2.imshow("color",img)
# cv2.waitKey(15000)
# cv2.destroyAllWindows()
# #
#
# #

def click_event(event,x,y,flag,param):
    if event == cv2.EVENT_LBUTTONDOWN:
        print(x,',',y)
        font = cv2.FONT_HERSHEY_SIMPLEX
        text= str(x)+','+str(y)
        cv2.putText(first_frame,text,(x,y),font,1,(255,255,0),2)
        cv2.imshow('Coordinate',first_frame)

video = cv2.VideoCapture("/home/sushant/PycharmProjects/opencv/sample_images/c_book.mp4")
_,first_frame = video.read()

cv2.imshow('Coordinate',first_frame)

cv2.setMouseCallback('Coordinate',click_event)
cv2.waitKey()
video.release()
cv2.destroyAllWindows()


import numpy as np
import pytest

from qutip import data as _data
from qutip import CoreOptions

@pytest.fixture(params=[_data.CSR, _data.Dense, _data.Dia], ids=["CSR", "Dense", "Dia"])
def datatype(request):
    return request.param

class Test_isherm:
    tol = 1e-12

    @pytest.mark.repeat(20)
    @pytest.mark.parametrize("density", (0.1, 0.8))
    @pytest.mark.parametrize("size", (10, 100))
    def test_random_equal_structure(self, datatype, size, density):
        # This is only going to be approximate, as entries generated onto the
        # diagonal will fill only one slot, not two, after addition with the
        # transpose.  The densities are arbitrary, though, so this doesn't
        # matter much.
        nnz = int(0.5 * size*size * density) or 1
        indices = np.triu_indices(size)
        choice = np.random.choice(indices[0].size, nnz, replace=False)
        indices = (indices[0][choice], indices[1][choice])

        # Real-symmetric matrices.
        base = np.zeros((size, size), dtype=np.complex128)
        base[indices] = np.random.rand(nnz)
        base += base.T.conj()
        base = _data.to(datatype, _data.create(base))
        assert _data.isherm(base)

        # Complex Hermitian matrices
        base = np.zeros((size, size), dtype=np.complex128)
        base[indices] = np.random.rand(nnz) + 1j*np.random.rand(nnz)
        base += base.T.conj()
        base = _data.to(datatype, _data.create(base))
        assert _data.isherm(base)

        # Complex skew-Hermitian matrices
        base = np.zeros((size, size), dtype=np.complex128)
        base[indices] = np.random.rand(nnz) + 1j*np.random.rand(nnz)
        base += base.T
        base = _data.to(datatype, _data.create(base))
        assert not _data.isherm(base)

    @pytest.mark.parametrize("cols", (2, 4))
    @pytest.mark.parametrize("rows", (1, 5))
    def test_nonsquare_shapes(self, datatype, rows, cols):
        real = _data.to(datatype, _data.create(np.random.rand(rows, cols)))
        assert not _data.isherm(real, self.tol)
        assert not _data.isherm(real.transpose(), self.tol)

        imag = _data.to(
            datatype,
            _data.create(np.random.rand(rows, cols) + 1j * np.random.rand(rows, cols)),
        )
        assert not _data.isherm(imag, self.tol)
        assert not _data.isherm(imag.transpose(), self.tol)

    def test_diagonal_elements(self, datatype):
        n = 10
        base = _data.to(datatype, _data.create(np.diag(np.random.rand(n))))
        assert _data.isherm(base, tol=self.tol)
        assert not _data.isherm(_data.mul(base, 1j), tol=self.tol)

    def test_compare_implicit_zero_structure(self, datatype):
        """
        Regression test for gh-1350, comparing explicitly stored values in the
        matrix (but below the tolerance for allowable Hermicity) to implicit
        zeros.
        """

        with CoreOptions(auto_tidyup=False):
            base = _data.to(
                datatype,
                _data.create(np.array([[1, self.tol * 1e-3j], [0, 1]]))
            )
        # If this first line fails, the zero has been stored explicitly and so
        # the test is invalid.
        assert np.count_nonzero(base.to_array()) == 3
        assert _data.isherm(base, tol=self.tol)
        assert _data.isherm(base.transpose(), tol=self.tol)

        # A similar test if the structures are different, but it's not
        # Hermitian.
        base = _data.to(datatype, _data.create(np.array([[1, 1j], [0, 1]])))
        assert np.count_nonzero(base.to_array()) == 3
        assert not _data.isherm(base, tol=self.tol)
        assert not _data.isherm(base.transpose(), tol=self.tol)

        # Catch possible edge case where it shouldn't be Hermitian, but faulty
        # loop logic doesn't fully compare all rows.
        base = _data.to(
            datatype,
            _data.create(
                np.array(
                    [
                        [0, 0, 0, 0],
                        [0, 0, 0, 0],
                        [0, 1, 0, 0],
                        [0, 0, 0, 0],
                    ],
                    dtype=np.complex128,
                )
            ),
        )
        assert np.count_nonzero(base.to_array()) == 1
        assert not _data.isherm(base, tol=self.tol)
        assert not _data.isherm(base.transpose(), tol=self.tol)

    @pytest.mark.parametrize("density", np.linspace(0.2, 1, 17))
    def test_compare_implicit_zero_random(self, datatype, density):
        """
        Regression test of gh-1350.

        Larger matrices where all off-diagonal elements are below the
        absolute tolerance, so everything should always appear Hermitian, but
        with random patterns of non-zero elements.  It doesn't matter that it
        isn't Hermitian if scaled up; everything is below absolute tolerance,
        so it should appear so.  We also set the diagonal to be larger to the
        tolerance to ensure isherm can't just compare everything to zero.
        """
        n = 10
        base = self.tol * 1e-2 * (np.random.rand(n, n) + 1j * np.random.rand(n, n))
        # Mask some values out to zero.
        base[np.random.rand(n, n) > density] = 0
        np.fill_diagonal(base, self.tol * 1000)
        nnz = np.count_nonzero(base)
        with CoreOptions(auto_tidyup=False):
            base = _data.to(datatype, _data.create(base))
        assert np.count_nonzero(base.to_array()) == nnz
        assert _data.isherm(base, tol=self.tol)
        assert _data.isherm(base.transpose(), tol=self.tol)

        # Similar test when it must be non-Hermitian.  We set the diagonal
        # to be real because we want to test off-diagonal implicit zeros,
        # and having an imaginary first element would automatically fail.
        nnz = 0
        while nnz <= n:
            # Ensure that we don't just have the real diagonal.
            base = self.tol * 1000j * np.random.rand(n, n)
            # Mask some values out to zero.
            base[np.random.rand(n, n) > density] = 0
            np.fill_diagonal(base, self.tol * 1000)
            nnz = np.count_nonzero(base)
        with CoreOptions(auto_tidyup=False):
            base = _data.to(datatype, _data.create(base))
        assert np.count_nonzero(base.to_array()) == nnz
        assert not _data.isherm(base, tol=self.tol)
        assert not _data.isherm(base.transpose(), tol=self.tol)

    def test_structure_detection(self, datatype):
        base = np.array([[1,1,0],
                         [0,1,1],
                         [1,0,1]])
        base = _data.to(datatype, _data.create(base))
        assert not _data.isherm(base, tol=self.tol)

class Test_isdiag:
    @pytest.mark.parametrize("shape",
        [(10, 1), (2, 5), (5, 2), (5, 5)]
    )
    def test_isdiag(self, shape, datatype):
        mat = np.zeros(shape)
        data = _data.to(datatype, _data.Dense(mat))
        # empty matrices are diagonal
        assert _data.isdiag(data)

        mat[0, 0] = 1
        data = _data.to(datatype, _data.Dense(mat))
        assert _data.isdiag(data)

        mat[1, 0] = 1
        data = _data.to(datatype, _data.Dense(mat))
        assert not _data.isdiag(data)


from data.repositories.postgres.entities import db
from domain.entities.trade import Trade

class TradeEntity(db.Model):
    __tablename__ = "trades"

    id = db.Column(db.Integer, primary_key=True, nullable=False)
    price = db.Column(db.Float, nullable=False)
    quantity = db.Column(db.Float, nullable=False)
    quote_quantity = db.Column(db.Float, nullable=False)
    time = db.Column(db.Integer, nullable=False)  # Unix epoch in ms
    is_buyer_maker = db.Column(db.Boolean, nullable=False)
    is_best_match = db.Column(db.Boolean, nullable=False)

    def __init__(self,
                 _id: int,
                 price: str,
                 qty: str,
                 quote_quantity: str,
                 time: int,
                 is_buyer_maker: bool,
                 is_best_match: bool):
        self.id = _id
        self.price = float(price)
        self.quantity = float(qty)
        self.quote_quantity = float(quote_quantity)
        self.time = int(time/1000)
        self.is_buyer_maker = is_buyer_maker
        self.is_best_match = is_best_match

    @classmethod
    def from_dict(cls, adict: dict):
        return TradeEntity(adict.get("id"),
                           adict.get("price"),
                           adict.get("qty"),
                           adict.get("quoteQty"),
                           adict.get("time"),
                           adict.get("isBuyerMaker"),
                           adict.get("isBestMatch"))

    def to_domain(self) -> Trade:
        return Trade(self.id,
                     self.price,
                     self.quantity,
                     self.quote_quantity,
                     self.time,
                     self.is_buyer_maker,
                     self.is_best_match)


import logging
from logging.handlers import TimedRotatingFileHandler
from datetime import date, timedelta, datetime

tfm = "%Y-%m-%d %H:%M:%S"
edit_cal = 0
msg_id = None

class MyTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, filename, when='midnight', interval=1, backupCount=0):
        super().__init__(filename=filename, when=when, interval=interval, backupCount=backupCount, encoding="utf-8")
        self.namer = rotator_namer

def rotator_namer(filename):
    yesterday = (date.today() - timedelta(1)).isoformat()
    return filename.split('.log')[0] + '_' + yesterday + '.log'

async def log_message(backend, log_msg, guild=None, channel='Server', command=None):
    logging.getLogger('discord').info(f"【{guild}】{log_msg} ({channel})")
    prefix = f'[{datetime.now().strftime(tfm)}][INFO ][{channel}]'
    if command:
        content = f"+ {prefix} {log_msg}\n"
    elif command == 0:
        content = f"- {prefix} {log_msg}\n"
    else:
        content = f"{prefix} {log_msg}\n"

    global edit_cal, msg_id
    if edit_cal % 10 == 0:
        edit_cal = 0
        msg = await backend.send(f"```diff\n{content}```")
        msg_id = msg.id
    else:
        msg = await backend.fetch_message(msg_id)
        original_content = msg.content
        updated_content = original_content[:-3] + f"{content}```"
        await msg.edit(content=updated_content)
    edit_cal += 1


import datetime
import random
import math
from re import L
import time

class client_manager:
    def __init__(self, game):
        """Creates a client_manager object. This object managers the leaderboards and interaction with the server"""
        self.game = game
        self.name = "player" + str(random.randint(100, 999))
        self.timeboards = [ #stores the leaderboard for times for each respective map
            [],
            [],
            []
        ]
        self.timeboard_positions = [-1, -1, -1]
        self.previous_times = [0, 0, 0]
        self.scoreboards = [ #stores the leaderboard for scores for each respective map
            [],
            [],
            []
        ]
        self.scoreboard_positions = [-1, -1, -1]
        self.previous_scores = [0, 0, 0]

    def sort(self, leaderboard, reverse_list = False):
        """Function to sort the leaderboards based on the times or scores of each player"""
        leaderboard.sort(key = lambda x: x[1])
        if reverse_list:
            leaderboard.reverse()

    def get_pos(self, leaderboard):
        """"Returns the player's current position on that leaderboard"""
        i = 0
        for position in leaderboard:
            current_player = position[0]
            if current_player == self.name:
                return i
            i = i + 1
        return -1

    def add_new_time(self, time, leaderboard_index):
        """Used to add a player's time to the leaderboard.
        \nTime is an integer represent the number of seconds
        \nLeaderboard index is the respective map leaderboard"""
        timeboard_pos = self.timeboard_positions[leaderboard_index]
        timeboard = self.timeboards[leaderboard_index]
        if timeboard_pos == -1:
            timeboard.append([self.name, time])
        else:
            self.previous_times[leaderboard_index] = timeboard.pop(timeboard_pos)[1]
            timeboard.append([self.name, time])
        self.sort(timeboard, True)
        self.timeboard_positions[leaderboard_index] = self.get_pos(timeboard)

    def add_new_score(self, score, leaderboard_index):
        """Used to add a player's score to the leaderboard.
        \nTime is an integer represent the number of seconds
        \nLeaderboard index is the respective map leaderboard"""
        scoreboard_pos = self.scoreboard_positions[leaderboard_index]
        scoreboard = self.scoreboards[leaderboard_index]
        #print(scoreboard_pos)
        #print(scoreboard)
        if scoreboard_pos == -1:
            scoreboard.append([self.name, score])
        else:
            self.previous_scores[leaderboard_index] = scoreboard.pop(scoreboard_pos)[1]
            scoreboard.append([self.name, score])
        self.sort(scoreboard, True)
        self.scoreboard_positions[leaderboard_index] = self.get_pos(scoreboard)
        #print(scoreboard)

    def send_update_scores(self):
        """Used to send a player's scores to the server."""
        code = 31
        pos = None
        score = None
        i = 0
        while(i < 3):
            pos = self.scoreboard_positions[i]
            score = self.scoreboards[i][pos][1]
            self.game.send_Q.put("{}><{}><{}".format(code + i, self.name, score))
            i = i + 1

    def send_update_times(self):
        """Used to send a player's times to the server."""
        code = 41
        pos = None
        i = 0
        while(i < 3):
            pos = self.timeboard_positions[i]
            score = self.timeboards[i][pos][1]
            self.game.send_Q.put("{}><{}><{}".format(code + i, self.name, score))
            i = i + 1

    def change_name(self, name):
        """Change the client's ID"""
        self.name = name

    def get_updated_timeboard(self, leaderboard_index):
        """Retrieve the latest leaderboards for times from the leaderboards.
        \nSends a request to the server. Will fail if there is an issue with the server connection."""
        code = 21 + leaderboard_index
        self.game.send_Q.put("{}><{}".format(code, self.name))
        while True:
            if  self.game.receive_Q.empty():
                pass
            else:
                self.timeboards[leaderboard_index] = self.response_to_leaderboard(self.game.receive_Q.get())
                self.timeboard_positions[leaderboard_index] = self.get_pos(self.timeboards[leaderboard_index])
                return self.timeboards[leaderboard_index]

    def get_updated_scoreboard(self, leaderboard_index):
        """Retrieve the latest leaderboards for scores from the leaderboards.
        \nSends a request to the server. Will fail if there is an issue with the server connection."""
        code = 11 + leaderboard_index
        self.game.send_Q.put("{}><{}".format(code, self.name))
        while True:
            if  self.game.receive_Q.empty():
                pass
            else:
                self.scoreboards[leaderboard_index] = self.response_to_leaderboard(self.game.receive_Q.get())
                self.scoreboard_positions[leaderboard_index] = self.get_pos(self.scoreboards[leaderboard_index])
                return True

    def get_score(self, leaderboard_index):
        """Get the player's score for that specific score leaderboard.
            \nLeaderboard index is the respective map leaderboard
        """
        score = 1
        place_in_leaderboard = self.scoreboard_positions[leaderboard_index]
        return self.scoreboards[leaderboard_index][place_in_leaderboard][score]

    def get_time(self, leaderboard_index):
        """Get the player's time for that specific score leaderboard.
            \nLeaderboard index is the respective map leaderboard
        """
        score = 1
        place_in_leaderboard = self.timeboard_positions[leaderboard_index]
        return self.timeboards[leaderboard_index][place_in_leaderboard][score]

    def seconds_to_min(self):
        """Converts seconds from the integer value to a minutes and seconds representation like: 02:36"""

        time_string = str(datetime.timedelta(seconds = self.timeboard_time))

        return time_string[2:]

    def response_to_leaderboard(self, response):
        """Converts the retrieve server response to a leaderboard for storage"""
        response = response.split("><")
        users = response[1].split(",")
        scores = response[2].split(",")
        scores = [int(score) for score in scores]
        leaderboard = []
        i = 0
        while (i < len(users)):
            leaderboard.append([users[i], scores[i]])
            i = i + 1
        return leaderboard

    def get_timeboard(self, leaderboard_index):
        """Returns the leaderboard for time.
        \nLeaderboard index is the respective map leaderboard"""
        return self.timeboards[leaderboard_index]

    def get_time_board_pos(self, leaderboard_index):
        """Returns the player's position on that time leaderboard.
        \nLeaderboard index is the respective map leaderboard"""
        return self.timeboard_positions[leaderboard_index] + 0

    def get_scoreboard(self, leaderboard_index):
        """Returns the leaderboard for score.
        \nLeaderboard index is the respective map leaderboard"""
        return self.scoreboards[leaderboard_index]

    def get_score_board_pos(self, leaderboard_index):
        """Returns the player's position on that score leaderboard.
        \nLeaderboard index is the respective map leaderboard"""
        return self.scoreboard_positions[leaderboard_index] + 0

    def calculate_final_time(self, time: int, num_of_lines = 0, num_of_steps = 0):
        """Calculates the player's final time
        \nWhere FINAL TIME = [time remaining to solve with a max of 2 minutes] + [code effeciency bonus with a max of 30 seconds]
        \nWhere [code effeciency bonus with a max of 30 seconds] = [time remaining to solve] + [1/log(number lines of code)*15 + 1/log(number of in-game steps taken)*15]
        \nIf num_of_steps or num_of_steps are not provided (or equal to zero) it returns just the time.
        \nPlease call this function when adding your score to the client_manager i.e. add_new_time(calculate_final_time(time, num_of_lines, num_of_steps), leaderboard_index)"""
        if num_of_lines == 0 or num_of_steps == 0:
            return time
        else:
            return time + ((1/(math.log(math.e, num_of_lines)))*15 + (1/(math.log(math.e, num_of_steps)))*15)

    def calculate_final_score(self,points):
        """Calculates the player's final score"""
        return points

    def print(self):
        """Prints the client manager field values in the console"""
        print("Client name: {0}".format(self.name))
        print("Timeboard Positions: {0}".format(self.timeboard_positions))
        print("Timeboard: {0}".format(self.timeboards))
        print("Scoreboard Positions: {0}".format(self.scoreboard_positions))
        print("Scoreboard {0}".format(self.scoreboards))


#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import click
import os
import time
import syslog
import subprocess
from ruijieconfig import MONITOR_CONST, FANCTROLDEBUG, MONITOR_FANS_LED, DEV_LEDS, MONITOR_PSU_STATUS, \
        MONITOR_SYS_PSU_LED, MONITOR_DEV_STATUS, MONITOR_FAN_STATUS, MONITOR_DEV_STATUS_DECODE, \
        MONITOR_SYS_FAN_LED, MONITOR_SYS_LED, fanloc

from ruijieutil import rji2cget, getMacTemp_sysfs, write_sysfs_value, get_sysfs_value, strtoint, \
        rji2cset

import traceback
import glob

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

DEBUG_COMMON     = 0x01
DEBUG_LEDCONTROL = 0x02
DEBUG_FANCONTROL = 0x04

class AliasedGroup(click.Group):
    def get_command(self, ctx, cmd_name):
        rv = click.Group.get_command(self, ctx, cmd_name)
        if rv is not None:
            return rv
        matches = [x for x in self.list_commands(ctx)
                   if x.startswith(cmd_name)]
        if not matches:
            return None
        elif len(matches) == 1:
            return click.Group.get_command(self, ctx, matches[0])
        ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))

def fanwarninglog(s):
    #s = s.decode('utf-8').encode('gb2312')
    syslog.openlog("FANCONTROL",syslog.LOG_PID)
    syslog.syslog(syslog.LOG_WARNING,s)

def fancriticallog(s):
    #s = s.decode('utf-8').encode('gb2312')
    syslog.openlog("FANCONTROL",syslog.LOG_PID)
    syslog.syslog(syslog.LOG_CRIT,s)

def fanerror(s):
    #s = s.decode('utf-8').encode('gb2312')
    syslog.openlog("FANCONTROL",syslog.LOG_PID)
    syslog.syslog(syslog.LOG_ERR,s)

def fanwarningdebuglog(debuglevel,s):
    #s = s.decode('utf-8').encode('gb2312')
    if FANCTROLDEBUG & debuglevel:
        syslog.openlog("FANCONTROL",syslog.LOG_PID)
        syslog.syslog(syslog.LOG_DEBUG,s)

class FanControl(object):
    critnum = 0
    def __init__(self):
        self._fanOKNum = 0
        self._psuOKNum = 0
        self._intemp = -100.0
        self._mac_aver = -100.0
        self._mac_max = -100.0
        self._preIntemp = -1000 # previous temperature
        self._outtemp = -100
        self._boardtemp = -100
        self._cputemp = -1000

    @property
    def fanOKNum(self):
        return self._fanOKNum;

    @property
    def psuOKNum(self):
        return self._psuOKNum;

    @property
    def cputemp(self):
        return self._cputemp;

    @property
    def intemp(self):
        return self._intemp;

    @property
    def outtemp(self):
        return self._outtemp;

    @property
    def boardtemp(self):
        return self._boardtemp;

    @property
    def mac_aver(self):
        return self._mac_aver;

    @property
    def preIntemp(self):
        return self._preIntemp;

    @property
    def mac_max(self):
        return self._mac_max;

    def sortCallback(self, element):
        return element['id']

    def gettemp(self,ret):
        u'''get inlet, outlet, hot-point and cpu temperature'''
        temp_conf = MONITOR_DEV_STATUS.get('temperature', None)

        if temp_conf is None:
            fanerror("gettemp: config error")
            return False
        for item_temp in temp_conf:
            try:
                retval = ""
                rval = None
                name = item_temp.get('name')
                location = item_temp.get('location')
                if name == "cpu":
                    L=[]
                    for dirpath, dirnames, filenames in os.walk(location):
                        for file in filenames :
                            if file.endswith("input"):
                                L.append(os.path.join(dirpath, file))
                        L =sorted(L,reverse=False)
                    for i in range(len(L)):
                        nameloc = "%s/temp%d_label"%(location,i+1)
                        valloc  = "%s/temp%d_input"%(location,i+1)
                        with open(nameloc, 'r') as fd1:
                            retval2 = fd1.read()
                        with open(valloc, 'r') as fd2:
                            retval3 = fd2.read()
                        ret_t ={}
                        ret_t["name"] = retval2.strip()
                        ret_t["value"] = float(retval3)/1000
                        ret.append(ret_t)
                        fanwarningdebuglog(DEBUG_COMMON,"gettemp %s : %f" % (ret_t["name"],ret_t["value"]))
                else:
                    locations = glob.glob(location)
                    with open(locations[0], 'r') as fd1:
                        retval = fd1.read()
                    rval = float(retval)/1000
                    ret_t ={}
                    ret_t["name"] = name
                    ret_t["value"] = rval
                    ret.append(ret_t)
                    fanwarningdebuglog(DEBUG_COMMON,"gettemp %s : %f" % (ret_t["name"],ret_t["value"]))
            except Exception as e:
                fanerror("gettemp error:name:%s" % name)
                fanerror(str(e))
        return True

    def checkslot(self,ret):
        u'''get slot present status'''
        slots_conf = MONITOR_DEV_STATUS.get('slots', None)
        slotpresent = MONITOR_DEV_STATUS_DECODE.get('slotpresent',None)

        if slots_conf is None or slotpresent is None:
            return False
        for item_slot in slots_conf:
            totalerr = 0
            try:
                ret_t = {}
                ret_t["id"] = item_slot.get('name')
                ret_t["status"] = ""
                gettype = item_slot.get('gettype')
                presentbit = item_slot.get('presentbit')
                if gettype == "io":
                    io_addr = item_slot.get('io_addr')
                    val = io_rd(io_addr)
                    if val is not None:
                        retval = val
                    else:
                        totalerr -= 1
                        fanerror(" %s  %s" % (item_slot.get('name'), "lpc read failed"))
                else:
                    bus = item_slot.get('bus')
                    loc = item_slot.get('loc')
                    offset = item_slot.get('offset')
                    ind, val = rji2cget(bus, loc,offset)
                    if ind == True:
                        retval = val
                    else:
                        totalerr -= 1
                        fanerror(" %s  %s" % (item_slot.get('name'), "i2c read failed"))
                if totalerr < 0 :
                    ret_t["status"] = "NOT OK"
                    ret.append(ret_t)
                    continue
                val_t = (int(retval,16) & (1<< presentbit)) >> presentbit
                fanwarningdebuglog(DEBUG_COMMON,"%s present:%s" % (item_slot.get('name'),slotpresent.get(val_t)))
                if val_t != slotpresent.get('okval'):
                    ret_t["status"] = "ABSENT"
                else:
                    ret_t["status"] = "PRESENT"
            except Exception as e:
                ret_t["status"] = "NOT OK"
                totalerr -= 1
                fanerror("checkslot error")
                fanerror(str(e))
            ret.append(ret_t)
        return True

    def checkpsu(self,ret):
        u'''get  psu status present, output and warning'''
        psus_conf = MONITOR_DEV_STATUS.get('psus', None)
        psupresent = MONITOR_DEV_STATUS_DECODE.get('psupresent',None)
        psuoutput = MONITOR_DEV_STATUS_DECODE.get('psuoutput',None)
        psualert = MONITOR_DEV_STATUS_DECODE.get('psualert',None)

        if psus_conf is None or psupresent is None or psuoutput is None:
            fanerror("checkpsu: config error")
            return False
        for item_psu in psus_conf:
            totalerr = 0
            try:
                ret_t = {}
                ret_t["id"] = item_psu.get('name')
                ret_t["status"] = ""
                gettype = item_psu.get('gettype')
                presentbit = item_psu.get('presentbit')
                statusbit = item_psu.get('statusbit')
                alertbit = item_psu.get('alertbit')
                if gettype == "io":
                    io_addr = item_psu.get('io_addr')
                    val = io_rd(io_addr)
                    if val is not None:
                        retval = val
                    else:
                        totalerr -= 1
                        fanerror(" %s  %s" % (item_psu.get('name'), "lpc read failed"))
                else:
                    bus = item_psu.get('bus')
                    loc = item_psu.get('loc')
                    offset = item_psu.get('offset')
                    ind, val = rji2cget(bus, loc,offset)
                    if ind == True:
                        retval = val
                    else:
                        totalerr -= 1
                        fanerror(" %s  %s" % (item_psu.get('name'), "i2c read failed"))
                if totalerr < 0 :
                    ret_t["status"] = "NOT OK"
                    ret.append(ret_t)
                    continue
                val_t = (int(retval,16) & (1<< presentbit)) >> presentbit
                val_status = (int(retval,16) & (1<< statusbit)) >> statusbit
                val_alert = (int(retval,16) & (1<< alertbit)) >> alertbit
                fanwarningdebuglog(DEBUG_COMMON,"%s present:%s output:%s alert:%s" % (item_psu.get('name'),psupresent.get(val_t),psuoutput.get(val_status),psualert.get(val_alert)))
                if val_t != psupresent.get('okval') or val_status != psuoutput.get('okval') or val_alert != psualert.get('okval'):
                    totalerr -=1
            except Exception as e:
                totalerr -= 1
                fanerror("checkpsu error")
                fanerror(str(e))
            if totalerr < 0:
                ret_t["status"] = "NOT OK"
            else:
                ret_t["status"] = "OK"
            ret.append(ret_t)
        return True

    def checkfan(self,ret):
        u'''get fan status present and roll'''
        fans_conf = MONITOR_DEV_STATUS.get('fans', None)
        fanpresent = MONITOR_DEV_STATUS_DECODE.get('fanpresent',None)
        fanroll = MONITOR_DEV_STATUS_DECODE.get('fanroll',None)

        if fans_conf is None or fanpresent is None or fanroll is None:
            fanerror("checkfan: config error")
            return False
        for item_fan in fans_conf:
            totalerr = 0
            try:
                ret_t = {}
                ret_t["id"] = item_fan.get('name')
                ret_t["status"] = ""
                presentstatus = item_fan.get('presentstatus')
                presentbus = presentstatus.get('bus')
                presentloc = presentstatus.get('loc')
                presentaddr = presentstatus.get('offset')
                presentbit = presentstatus.get('bit')
                ind, val = rji2cget(presentbus, presentloc,presentaddr)
                if ind == True:
                    val_t = (int(val,16) & (1<< presentbit)) >> presentbit
                    fanwarningdebuglog(DEBUG_COMMON,"checkfan:%s present status:%s" % (item_fan.get('name'),fanpresent.get(val_t)))
                    if val_t != fanpresent.get('okval'):
                        ret_t["status"] = "ABSENT"
                        ret.append(ret_t)
                        continue
                else:
                    fanerror("checkfan: %s get present status error." % item_fan.get('name'))
                motors = item_fan.get("rollstatus")
                for motor in motors:
                    statusbus = motor.get('bus', None)
                    statusloc = motor.get('loc', None)
                    statusaddr = motor.get('offset', None)
                    statusbit = motor.get('bit', None)
                    ind, val = rji2cget(statusbus, statusloc, statusaddr)
                    if ind == True:
                        val_t = (int(val,16) & (1<< statusbit)) >> statusbit
                        fanwarningdebuglog(DEBUG_COMMON,"checkfan:%s roll status:%s" % (motor.get('name'),fanroll.get(val_t)))
                        if val_t != fanroll.get('okval'):
                            totalerr -= 1
                    else:
                        totalerr -= 1
                        fanerror("checkfan: %s " % item_fan.get('name'))
                        fanerror("get %s status error." % motor["name"])
            except Exception as e:
                totalerr -= 1
                fanerror("checkfan error")
                fanerror(str(e))
            if totalerr < 0:
                ret_t["status"] = "NOT OK"
            else:
                ret_t["status"] = "OK"
            ret.append(ret_t)
        return True

    def getCurrentSpeed(self):
        try:
            loc = fanloc[0].get("location","")
            sped = get_sysfs_value(loc)
            value = strtoint(sped)
            return value
        except Exception as e:
            fanerror("%%policy: get current speedlevel error")
            fanerror(str(e))
            return None

    # guarantee the speed is lowest when speed lower than lowest value after speed-adjustment
    def checkCurrentSpeedSet(self):
        fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy: guarantee the lowest speed after speed-adjustment")
        value =  self.getCurrentSpeed()
        if value is None or value == 0:
            raise Exception("%%policy: getCurrentSpeed None")
        elif value < MONITOR_CONST.MIN_SPEED:
            self.fanSpeedSet(MONITOR_CONST.MIN_SPEED)

    def fanSpeedSet(self, level):
        if level >= MONITOR_CONST.MAX_SPEED:
            level = MONITOR_CONST.MAX_SPEED
        for item in fanloc:
            try:
                loc = item.get("location","")
                write_sysfs_value(loc, "0x%02x"% level )
            except Exception as e:
                fanerror(str(e))
                fanerror("%%policy: config fan runlevel error")
        self.checkCurrentSpeedSet() # guaranteed minimum

    def fanSpeedSetMax(self):
        try:
            self.fanSpeedSet(MONITOR_CONST.MAX_SPEED)
        except Exception as e:
            fanerror("%%policy:fanSpeedSetMax failed")
            fanerror(str(e))

    def fanStatusCheck(self): # fan status check , max speed if fan error
        if self.fanOKNum < MONITOR_CONST.FAN_TOTAL_NUM:
            fanwarninglog("%%DEV_MONITOR-FAN: Normal fan number: %d" % (self.fanOKNum))
            self.fanSpeedSetMax()
            return False
        return True

    def setFanAttr(self,val):
        u'''set status of each fan'''
        for item in val:
            fanid = item.get("id")
            fanattr = fanid + "status"
            fanstatus = item.get("status")
            setattr(FanControl,fanattr,fanstatus)
            fanwarningdebuglog(DEBUG_COMMON,"fanattr:%s,fanstatus:%s"% (fanattr,fanstatus))

    def getFanPresentNum(self,curFanStatus):
        fanoknum = 0;
        for item in curFanStatus:
            if item["status"] == "OK":
                fanoknum += 1
        self._fanOKNum = fanoknum
        fanwarningdebuglog(DEBUG_COMMON,"fanOKNum = %d"% self._fanOKNum)

    def getFanStatus(self):
        try:
            curFanStatus = []
            ret = self.checkfan(curFanStatus)
            if ret == True:
                self.setFanAttr(curFanStatus)
                self.getFanPresentNum(curFanStatus)
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getFanStatus success" )
                return 0
        except AttributeError as e:
            fanerror(str(e))
        except Exception as e:
            fanerror(str(e))
        return -1

    def getPsuOkNum(self,curPsuStatus):
        psuoknum = 0;
        for item in curPsuStatus:
            if item.get("status") == "OK":
                psuoknum += 1
        self._psuOKNum = psuoknum
        fanwarningdebuglog(DEBUG_COMMON,"psuOKNum = %d"% self._psuOKNum)

    def getPsuStatus(self):
        try:
            curPsuStatus = []
            ret = self.checkpsu(curPsuStatus)
            if ret == True:
                self.getPsuOkNum(curPsuStatus)
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getPsuStatus success" )
                return 0
        except AttributeError as e:
            fanerror(str(e))
        except Exception as e:
            fanerror(str(e))
        return -1

    def getMonitorTemp(self, temp):
        for item in temp:
            if item.get('name') == "lm75in":
                self._intemp = item.get('value',self._intemp)
            if item.get('name') == "lm75out":
                self._outtemp = item.get('value',self._outtemp)
            if item.get('name') == "lm75hot":
                self._boardtemp = item.get('value',self._boardtemp)
            if item.get('name') == "Physical id 0":
                self._cputemp = item.get('value',self._cputemp)
        fanwarningdebuglog(DEBUG_COMMON,"intemp:%f, outtemp:%f, boadrtemp:%f, cputemp:%f"% (self._intemp,self._outtemp,self._boardtemp,self._cputemp))

    def getTempStatus(self):
        try:
            monitortemp =[]
            ret = self.gettemp(monitortemp)
            if ret == True:
                self.getMonitorTemp(monitortemp)
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getTempStatus success" )
                return 0
        except AttributeError as e:
            fanerror(str(e))
        except Exception as e:
            fanerror(str(e))
        return -1

    def getMacStatus_bcmcmd(self):
        try:
            if waitForDocker(timeout = 0) == True :
                sta, ret = getMacTemp()
                if sta == True:
                    self._mac_aver = float(ret.get("average",self._mac_aver))
                    self._mac_max  = float(ret.get("maximum",self._mac_max))
                    fanwarningdebuglog(DEBUG_COMMON,"mac_aver:%f, mac_max:%f" % (self.mac_aver,self._mac_max))
                else:
                    fanwarningdebuglog(DEBUG_COMMON,"%%policy:getMacStatus_bcmcmd failed" )
            else:
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getMacStatus_bcmcmd SDK not OK" )
            return 0
        except AttributeError as e:
            fanerror(str(e))
        return -1

    def getMacStatus_sysfs(self,conf):
        try:
            sta, ret = getMacTemp_sysfs(conf)
            if sta == True:
                self._mac_aver = float(ret) / 1000
                self._mac_max = float(ret) / 1000
                fanwarningdebuglog(DEBUG_COMMON,"mac_aver:%f, mac_max:%f" % (self.mac_aver,self._mac_max))
            elif conf.get("try_bcmcmd", 0) == 1:
                fanwarningdebuglog(DEBUG_COMMON,"get sysfs mac temp failed.try to use bcmcmd")
                self.getMacStatus_bcmcmd()
            else:
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getMacStatus_sysfs failed" )
            return 0
        except AttributeError as e:
            fanerror(str(e))
        return -1

    def getMacStatus(self):
        try:
            mactempconf = MONITOR_DEV_STATUS.get('mac_temp', None)
            if mactempconf is not None:
                self.getMacStatus_sysfs(mactempconf)
            else:
                self.getMacStatus_bcmcmd()
            return 0
        except AttributeError as e:
            fanerror(str(e))
        return -1

    def settSlotAttr(self,val):
        u'''set each slot present status attribute'''
        for item in val:
            slotid = item.get("id")
            slotattr = slotid + "status"
            slotstatus = item.get("status")
            setattr(FanControl,slotattr,slotstatus)
            fanwarningdebuglog(DEBUG_COMMON,"slotattr:%s,slotstatus:%s"% (slotattr,slotstatus))

    def getSlotStatus(self):
        try:
            curSlotStatus = []
            ret = self.checkslot(curSlotStatus)
            if ret == True:
                self.settSlotAttr(curSlotStatus)
                fanwarningdebuglog(DEBUG_COMMON,"%%policy:getSlotStatus success" )
        except AttributeError as e:
            fanerror(str(e))
        return 0

    def fanctrol(self): #fan speed-adjustment
        try:
            if self.preIntemp <= -1000:
                self.preIntemp = self.intemp
            fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:previous temperature[%.2f] , current temperature[%.2f]" % (self.preIntemp,self.intemp))
            if self.intemp < MONITOR_CONST.TEMP_MIN:
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:inlet  %.2f  minimum temperature: %.2f" %(self.intemp,MONITOR_CONST.TEMP_MIN))
                self.fanSpeedSet(MONITOR_CONST.DEFAULT_SPEED) # default level
            elif self.intemp >=  MONITOR_CONST.TEMP_MIN and self.intemp > self.preIntemp:
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:increase temperature")
                self.policySpeed(self.intemp)
            elif self.intemp >=  MONITOR_CONST.TEMP_MIN and (self.preIntemp - self.intemp) > MONITOR_CONST.MONITOR_FALL_TEMP:
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:temperature reduce over %d degree" % MONITOR_CONST.MONITOR_FALL_TEMP)
                self.policySpeed(self.intemp)
            else:
                speed = self.getCurrentSpeed()# set according to current speed, prevent fan watch-dog
                if speed is not None:
                    self.fanSpeedSet(speed)
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:change nothing")
        except Exception as e:
            fanerror("%%policy: fancontrol error")

    # start speed-adjustment
    def startFanCtrol(self):
        self.checkCrit()
        if self.critnum == 0 and self.checkWarning() == False and self.fanStatusCheck() ==True:
            self.fanctrol()
            self.checkDevError()
        fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy: speed after speed-adjustment is %0x" % (self.getCurrentSpeed()))

    def policySpeed(self, temp): # fan speed-adjustment algorithm
        fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:fan speed-adjustment algorithm")
        sped_level = MONITOR_CONST.DEFAULT_SPEED + MONITOR_CONST.K * (temp - MONITOR_CONST.TEMP_MIN)
        self.fanSpeedSet(sped_level)
        self.preIntemp = self.intemp

    def getBoardMonitorMsg(self,ledcontrol = False):
        ret_t = 0
        try:
            ret_t += self.getFanStatus()  # get fan status, get number of fan which status is OK
            ret_t += self.getTempStatus() # get inlet, outlet, hot-point temperature, CPU temperature
            ret_t += self.getMacStatus()  # get MAC highest and average temperature
            if ledcontrol == True:
                ret_t += self.getSlotStatus() # get slot present status
                ret_t += self.getPsuStatus()  # get psu status
            if ret_t == 0:
                return True
        except Exception as e:
            fanerror(str(e))
        return False

    # device error algorithm    Tmac-Tin≥50℃, or Tmac-Tin≤-50℃
    def checkDevError(self):
        try:
            if (self.mac_aver - self.intemp) >= MONITOR_CONST.MAC_UP_TEMP or (self.mac_aver - self.intemp) <= MONITOR_CONST.MAC_LOWER_TEMP:
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%DEV_MONITOR-TEMP: MAC temp get failed.")
                value =  self.getCurrentSpeed()
                if MONITOR_CONST.MAC_ERROR_SPEED >= value:
                    self.fanSpeedSet(MONITOR_CONST.MAC_ERROR_SPEED)
                else:
                    self.fanSpeedSetMax()
            else:
                pass
        except Exception as e:
            fanerror("%%policy:checkDevError failed")
            fanerror(str(e))

    def checkTempWarning(self):
        u'''check whether temperature above the normal alarm value'''
        try:
            if self._mac_aver >= MONITOR_CONST.MAC_WARNING_THRESHOLD \
            or self._outtemp >= MONITOR_CONST.OUTTEMP_WARNING_THRESHOLD \
            or self._boardtemp >= MONITOR_CONST.BOARDTEMP_WARNING_THRESHOLD \
            or self._cputemp>=MONITOR_CONST.CPUTEMP_WARNING_THRESHOLD \
            or self._intemp >=MONITOR_CONST.INTEMP_WARNING_THRESHOLD:
                fanwarningdebuglog(DEBUG_COMMON,"check whether temperature above the normal alarm value")
                return True
        except Exception as e:
            fanerror("%%policy: checkTempWarning failed")
            fanerror(str(e))
        return False

    def checkTempCrit(self):
        u'''check whether temperature above the critical alarm value'''
        try:
            if self._mac_aver >= MONITOR_CONST.MAC_CRITICAL_THRESHOLD \
            or ( self._outtemp >= MONITOR_CONST.OUTTEMP_CRITICAL_THRESHOLD \
            and self._boardtemp >= MONITOR_CONST.BOARDTEMP_CRITICAL_THRESHOLD \
            and self._cputemp>= MONITOR_CONST.CPUTEMP_CRITICAL_THRESHOLD \
            and self._intemp >= MONITOR_CONST.INTEMP_CRITICAL_THRESHOLD):
                fanwarningdebuglog(DEBUG_COMMON,"temperature above the critical alarm value")
                return True
        except Exception as e:
            fanerror("%%policy: checkTempCrit failed")
            fanerror(str(e))
        return False

    def checkFanStatus(self):
        u'''check fan status'''
        for item in MONITOR_FAN_STATUS:
            maxoknum = item.get('maxOkNum')
            minoknum = item.get('minOkNum')
            status = item.get('status')
            if self.fanOKNum >= minoknum and self.fanOKNum <= maxoknum :
                fanwarningdebuglog(DEBUG_COMMON,"checkFanStatus:fanOKNum:%d,status:%s" % (self.fanOKNum,status))
                return status
        fanwarningdebuglog(DEBUG_COMMON,"checkFanStatus Error:fanOKNum:%d" % (self.fanOKNum))
        return None

    def checkPsuStatus(self):
        u'''check psu status'''
        for item in MONITOR_PSU_STATUS:
            maxoknum = item.get('maxOkNum')
            minoknum = item.get('minOkNum')
            status = item.get('status')
            if self.psuOKNum >= minoknum and self.psuOKNum <= maxoknum :
                fanwarningdebuglog(DEBUG_COMMON,"checkPsuStatus:psuOKNum:%d,status:%s" % (self.psuOKNum,status))
                return status
        fanwarningdebuglog(DEBUG_COMMON,"checkPsuStatus Error:psuOKNum:%d" % (self.psuOKNum))
        return None

    def dealSysLedStatus(self):
        u'''set up SYSLED according to temperature, fan and psu status'''
        try:
            fanstatus = self.checkFanStatus()
            psustatus = self.checkPsuStatus()
            if self.checkTempCrit() == True or fanstatus == "red" or psustatus == "red":
                status = "red"
            elif self.checkTempWarning() == True or fanstatus == "yellow" or psustatus == "yellow":
                status = "yellow"
            else:
                status = "green"
            self.setSysLed(status)
            fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:dealSysLedStatus success, status:%s," % status)
        except Exception as e:
            fanerror(str(e))

    def dealSysFanLedStatus(self):
        u'''light panel fan led according to status'''
        try:
            status = self.checkFanStatus()
            if status is not None:
                self.setSysFanLed(status)
                fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:dealSysFanLedStatus success, status:%s," % status)
        except Exception as e:
            fanerror("%%ledcontrol:dealSysLedStatus error")
            fanerror(str(e))

    def dealPsuLedStatus(self):
        u'''set up PSU-LED according to psu status'''
        try:
            status = self.checkPsuStatus()
            if status is not None:
                self.setSysPsuLed(status)
            fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:dealPsuLedStatus success, status:%s," % status)
        except Exception as e:
            fanerror("%%ledcontrol:dealPsuLedStatus error")
            fanerror(str(e))

    def dealLocFanLedStatus(self):
        u'''light fan led according to fan status'''
        for item in MONITOR_FANS_LED:
            try:
                index = MONITOR_FANS_LED.index(item) + 1
                fanattr = "fan%dstatus" % index
                val_t = getattr(FanControl,fanattr,None)
                if val_t == "NOT OK":
                    rji2cset(item["bus"],item["devno"],item["addr"], item["red"])
                elif val_t == "OK":
                    rji2cset(item["bus"],item["devno"],item["addr"], item["green"])
                else:
                    pass
                fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:dealLocFanLed success.fanattr:%s, status:%s"% (fanattr,val_t))
            except Exception as e:
                fanerror("%%ledcontrol:dealLocFanLedStatus error")
                fanerror(str(e))

    def dealSlotLedStatus(self):
        u'''light slot status led according to slot present status'''
        slotLedList = DEV_LEDS.get("SLOTLED",[])
        for item in slotLedList:
            try:
                index = slotLedList.index(item) + 1
                slotattr = "slot%dstatus" % index
                val_t = getattr(FanControl,slotattr,None)
                if val_t == "PRESENT":
                    rji2cset(item["bus"],item["devno"],item["addr"], item["green"])
                fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:dealSlotLedStatus success.slotattr:%s, status:%s"% (slotattr,val_t))
            except Exception as e:
                fanerror("%%ledcontrol:dealSlotLedStatus error")
                fanerror(str(e))

    def dealBmcLedstatus(self,val):
        pass

    def dealLctLedstatus(self,val):
        pass

    def setled(self, item, color):
        if item.get('type', 'i2c') == 'sysfs':
            rjsysset(item["cmdstr"], item.get(color))
        else :
            mask = item.get('mask', 0xff)
            ind, val = rji2cget(item["bus"], item["devno"], item["addr"])
            if ind == True:
                setval = (int(val,16) & ~mask) | item.get(color)
                rji2cset(item["bus"], item["devno"], item["addr"], setval)
            else:
                fanerror("led %s" % "i2c read failed")

    def setSysLed(self,color):
        for item in MONITOR_SYS_LED:
            self.setled(item, color)

    def setSysFanLed(self,color):
        for item in MONITOR_SYS_FAN_LED:
            self.setled(item, color)

    def setSysPsuLed(self,color):
        for item in MONITOR_SYS_PSU_LED:
            self.setled(item, color)

    def checkWarning(self):
        try:
            if self.checkTempWarning() == True:
                fanwarningdebuglog(DEBUG_FANCONTROL,"anti-shake start")
                time.sleep(MONITOR_CONST.SHAKE_TIME)
                fanwarningdebuglog(DEBUG_FANCONTROL,"anti-shake end")
                self.getBoardMonitorMsg()# re-read
                if self.checkTempWarning() == True:
                    fanwarninglog("%%DEV_MONITOR-TEMP:The temperature of device is over warning value.")
                    self.fanSpeedSetMax()  # fan full speed
                    return True
        except Exception as e:
            fanerror("%%policy: checkWarning failed")
            fanerror(str(e))
        return False

    def checkCrit(self):
        try:
            if self.checkTempCrit() == True:
                fanwarningdebuglog(DEBUG_FANCONTROL,"anti-shake start")
                time.sleep(MONITOR_CONST.SHAKE_TIME)
                fanwarningdebuglog(DEBUG_FANCONTROL,"anti-shake end")
                self.getBoardMonitorMsg()# re-read
                if self.checkTempCrit() == True:
                    fancriticallog("%%DEV_MONITOR-TEMP:The temperature of device is over critical value.")
                    self.fanSpeedSetMax()  # fan full speed
                    self.critnum += 1 # anti-shake
                    if self.critnum >= MONITOR_CONST.CRITICAL_NUM:
                        subprocess.call(["reboot"])
                    fanwarningdebuglog(DEBUG_FANCONTROL,"crit次数:%d" % self.critnum)
                else:
                    self.critnum = 0
            else:
                self.critnum = 0
        except Exception as e:
            fanerror("%%policy: checkCrit failed")
            fanerror(str(e))

def callback():
    pass

def doFanCtrol(fanCtrol):
    ret = fanCtrol.getBoardMonitorMsg()
    if ret==True:
        fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:startFanCtrol")
        fanCtrol.startFanCtrol()
    else:
        fanCtrol.fanSpeedSetMax()
        fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:getBoardMonitorMsg error")

def doLedCtrol(fanCtrol):
    fanCtrol.getBoardMonitorMsg(ledcontrol = True) # get status
    fanCtrol.dealSysLedStatus()        # light system led
    fanCtrol.dealSysFanLedStatus()     # light panel fan led
    fanCtrol.dealLocFanLedStatus()     # light fan led
    fanCtrol.dealPsuLedStatus()        # light psu led
    fanCtrol.dealSlotLedStatus()       # light slot status led
    fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:doLedCtrol success")

def run(interval, fanCtrol):
    loop = 0
    # waitForDocker()
    while True:
        try:
            if loop % MONITOR_CONST.MONITOR_INTERVAL ==0: # fan speed-adjustment
                fanwarningdebuglog(DEBUG_FANCONTROL,"%%policy:fanCtrol")
                doFanCtrol(fanCtrol)
            else:
                fanwarningdebuglog(DEBUG_LEDCONTROL,"%%ledcontrol:start ledctrol")# LED control
                doLedCtrol(fanCtrol)
            time.sleep(interval)
            loop += interval
        except Exception as e:
            traceback.print_exc()
            fanerror(str(e))

@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS)
def main():
    '''device operator'''
    pass

@main.command()
def start():
    '''start fan control'''
    fanwarninglog("FANCTROL start")
    fanCtrol = FanControl()
    interval = MONITOR_CONST.MONITOR_INTERVAL /30
    run(interval, fanCtrol)

@main.command()
def stop():
    '''stop fan control '''
    fanwarninglog("stop")

##device_i2c operation
if __name__ == '__main__':
    main()


from typing import List

class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        if rowIndex == 0:
            return [1]
        elif rowIndex == 1:
            return [1, 1]
        prev = [1, 1]
        i = 2
        while i <= rowIndex:
            curr = [1]
            for j in range(len(prev) - 1):
                curr.append(prev[j] + prev[j + 1])
            curr.append(1)
            prev = curr
            i += 1
        return prev


import torch
from objective import contrastive_loss, modified_contrastive_loss
from tqdm import tqdm
from torch import nn
import json
from utils.visualizations import plot_loss_acc

with open('utils/configs.json') as f:
    configs = json.load(f)

def test_auxi_classification(model,
                             loader_val,
                             epoch,
                             device,
                             accum_steps,
                             temperature,
                             loss_fn):
    """
    Test the classification model in SimCLR.
    """
    loss_lst, acc_lst = [], []
    model.eval()
    with torch.no_grad():
        for x1, x2, _ in loader_val:
            x1 = x1.to(device=device, dtype=torch.float32)
            x2 = x2.to(device=device, dtype=torch.float32)
            _, z1 = model(x1)
            _, z2 = model(x2)
            loss, acc = loss_fn(z1, z2, temperature=temperature)
            loss /= accum_steps
            loss_lst.append(loss)
            acc_lst.append(acc)
        # Calculate average loss and accuracy
        avg_loss = sum(loss_lst) / len(loss_lst)
        avg_acc = sum(acc_lst) / len(acc_lst)
    print("Epoch: {} | avg valid loss: {:.4f} | avg valid accuracy: {:.4f}%".format(epoch + 1, avg_loss, avg_acc))
    return avg_loss, avg_acc

def train_simclr(model,
                 optimizer,
                 loader_train,
                 loader_val,
                 n_epochs,
                 device,
                 accum_steps,
                 temperature,
                 save_every,
                 modified_loss=False,
                 save_ckpt=True,
                 checkpt_path=None,
                 dataset_name='',
                 path_ext=''):
    """
    Pretrain a SimCLR model with ResNet50 as the encoder.

    Args:
    model: A SimCLRMain model.
    optimizer: An Optimizer object we will use to train the model.
    loader_train (DataLoader): dataloader containing training data.
    loader_val (DataLoader): dataloader containing data for validation.
    temperature (float): temperature used in NT-XENT.
    epochs (int): the number of epochs to train for.
    device (torch.device): 'cuda' or 'cpu' depending on the availability of GPU.
    accum_steps (int): number of steps to accumulate gradients.
    save_every (int): frequency for saving the model.
    modified_loss (bool): Indicate if we want to use the modified loss.
    save_ckpt (bool): indicate whether to save checkpoints.
    checkpt_path (str): if resuming training from a checkpoint, provide the path.
    dataset_name (str): to use in saved model names.
    path_ext (str): path for saving models.
    Returns: Nothing.
    """
    if checkpt_path is not None:
        checkpoint = torch.load(checkpt_path, map_location=device)
        model.load_state_dict(checkpoint['model_state_dict'])
        model.to(device)
        optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
        current_epoch = checkpoint['epoch']
        loss_array = checkpoint['losses']
        acc_array = checkpoint['accuracies']
    else:
        current_epoch = 0
        loss_array = {'train': [], 'valid': []}
        acc_array = {'train': [], 'valid': []}

    total_batch_size = int(configs["batch_size_small"] * accum_steps)

    if modified_loss:
        loss_fn = modified_contrastive_loss
        # TODO: make this more flexible
        ckpt_name = "simclr_mod_loss_ckpt_bs{}_nepoch{}_{}_temp{}.pth"
        model_name = "simclr_mod_loss_bs{}_nepoch{}_{}_temp{}.pth"
    else:
        loss_fn = contrastive_loss
        ckpt_name = "simclr_ckpt_bs{}_nepoch{}_{}_temp{}.pth"
        model_name = "simclr_model_bs{}_nepoch{}_{}_temp{}.pth"

    # For saving the model
    sample_inputs, _, _ = next(iter(loader_train))
    fixed_input = sample_inputs[:32, :, :, :]

    optimizer.zero_grad()
    print_every = int(len(loader_train) / 4)
    model = model.to(device=device)
    for e in range(current_epoch, n_epochs):
        train_loss, train_acc = [], []
        for t, (x1, x2, _) in enumerate(loader_train):
            model.train()
            x1 = x1.to(device=device, dtype=torch.float32)
            x2 = x2.to(device=device, dtype=torch.float32)
            _, z1 = model(x1)
            _, z2 = model(x2)
            loss, acc = loss_fn(z1, z2, temperature=temperature)
            loss /= accum_steps
            train_loss.append(loss.item())
            train_acc.append(acc)
            # Gradient accumulation
            loss.backward()
            if (t + 1) % accum_steps == 0:
                optimizer.step()
                optimizer.zero_grad()

            if t % print_every == 0:
                print(
                    'Epoch: {} | Iteration {} | Loss = {:.4f} | Accuracy = {:.4f}%'.format(e + 1, t, loss.item(), acc))
        # Evaluate the model after each epoch
        val_loss, val_acc = test_auxi_classification(model,
                                                     loader_val,
                                                     epoch=e,
                                                     device=device,
                                                     accum_steps=accum_steps,
                                                     temperature=temperature,
                                                     loss_fn=loss_fn)
        # Keep track of metrics during training and testing
        loss_array['train'].append(sum(train_loss) / len(train_loss))
        loss_array['valid'].append(val_loss.item())
        acc_array['train'].append(sum(train_acc) / len(train_acc))
        acc_array['valid'].append(val_acc)
        if save_ckpt:
            if (e + 1) % save_every == 0:
                torch.save({
                    'epoch': e,
                    'model_state_dict': model.state_dict(),
                    'optimizer_state_dict': optimizer.state_dict(),
                    'losses': loss_array,
                    'accuracies': acc_array,
                }, configs["doc_ckpt_path"] + ckpt_name.format(
                    total_batch_size,
                    (e + 1),
                    dataset_name,
                    str(temperature).replace('.', ''))
                )
        else:
            pass

    # Plot loss and accuracy
    plot_loss_acc(loss=loss_array["train"], accuracy=acc_array['train'],
                  title="acc_loss_train", save_plot=True
                  )
    plot_loss_acc(loss=loss_array["valid"], accuracy=acc_array['valid'],
                  title="acc_loss_valid", save_plot=True)
    # save the model
    model.eval()
    with torch.no_grad():
        torch.jit.save(torch.jit.trace(model, fixed_input.to(device), check_trace=False),
                       path_ext + model_name.format(
                           total_batch_size,
                           n_epochs,
                           dataset_name,
                           str(temperature).replace('.', ''), str())
                       )

def feature_extraction(simclr_model,
                       device,
                       loader):
    """
    Extract features from a pretrained SimCLR model using training dataset
    extracted in mode 'lin_eval'.
    Args:
        simclr_model: pretrained SimCLRMain.
        device (torch.device): 'cuda' or 'cpu' (depending on the availability of a gpu).
        loader: dataloader with data extracted with the function
                         get_augmented_dataloader in mode 'lin_eval'
                         (training set without augmentation)
    Returns: extracted features with corresponding labels for each image.
    """
    simclr_model.eval()
    features, targets = [], []
    with torch.no_grad():
        for img, tgt in tqdm(loader, desc='extracting features ...'):
            targets.append(tgt)
            img = img.to(device=device, dtype=torch.float32)
            feature, _ = simclr_model(img)
            features.append(feature)
        features_all = torch.cat(features, dim=0)
        targets_all = torch.cat(targets, dim=0)
    return features_all, targets_all

def test_lin_eval(clf_model,
                  simclr_model,
                  loader_test,
                  device):
    """
    Extract features with the pretrained simclr_model and test clf_model.
    Args:
        clf_model: trained linear classifier.
        simclr_model: pretrained simclr model.
        loader_test: data loader with test data.
        device (torch.device): 'cuda' or 'cpu'.
    Returns:
        loss: CE loss.
        top1_acc: accuracy.
    """
    features_valid, targets_valid = feature_extraction(
        simclr_model=simclr_model,
        device=device,
        loader=loader_test
    )
    clf_model.eval()
    loss_fn = torch.nn.CrossEntropyLoss()
    with torch.no_grad():
        preds = clf_model(features_valid)
        targets = targets_valid.to(device=device, dtype=torch.long)
        loss = loss_fn(preds, targets)
        _, predicted = preds.max(1)
        correct = predicted.eq(targets).sum().item()
        top1_acc = 100. * correct / targets.shape[0]
        # Do not print top5 accuracy for CIFAR10 because with only 10 classes it is always going to be quite high
        # top5_preds = torch.topk(preds, k=5, dim=1).indices
        # top5_correct = sum([targets[i] in top5_preds[i] for i in range(targets.shape[0])])
        # top5_acc = 100. * top5_correct / targets.shape[0]
    print(
        'Test Loss: {:.4f} | Test Top 1 Accuracy: {:.4f}%'.format(loss, top1_acc)
    )
    return loss, top1_acc

def train_lin_eval(features,
                   targets,
                   device,
                   representation_dim,
                   reg_weight,
                   n_step,
                   n_class=10
                   ):
    """
    Train a linear classification model with LBFGS and L2 regularization using
    features extracted from the pretrained SimCLR model.
    Args:
        features (tensor): extracted features.
        targets (tensor): indexes of corresponding class of images from which
                         the features were extracted.
        device (torch.device): 'cuda' or 'cpu'.
        representation_dim (int): dimension of features
                                  (dimension of the inputs to the projection head).
        reg_weight (float): weight of L2 regularization.
        n_step (int): number of epochs for training the linear evaluation.
        n_class (int): number of classes.
    Returns: the trained linear classifier.
    """
    linear_clf = nn.Sequential(
        nn.Flatten(),
        nn.Linear(in_features=representation_dim,
                  out_features=n_class)
    ).to(device)

    linear_clf.train()
    lbfgs_optim = torch.optim.LBFGS(linear_clf.parameters(), max_iter=configs['lbfgs_max_iter'])
    loss_fn = torch.nn.CrossEntropyLoss()
    iterator = tqdm(enumerate(range(n_step)), desc='Training the Linear Classification Model ...')
    for i, _ in iterator:
        # From Pytorch docs: Some optimization algorithms such as Conjugate Gradient
        # and LBFGS need to reevaluate the function multiple times, so you have to
        # pass in a closure that allows them to recompute your model. The closure
        # should clear the gradients, compute the loss, and return it.
        def closure():
            lbfgs_optim.zero_grad()
            preds = linear_clf(features)
            targets_device = targets.to(device=preds.device, dtype=torch.long)
            loss = loss_fn(preds, targets_device)
            # add regularization
            loss += reg_weight * linear_clf[1].weight.pow(2).sum()
            loss.backward()
            _, predicted = preds.max(1)
            correct = predicted.eq(targets_device).sum().item()
            iterator.set_description('epoch {}: Loss: {:.3f} | Train Acc: {:.3f}%'.format((i + 1), loss,
                                                                                100. * correct /
                                                                                targets_device.shape[0]))
            return loss

        lbfgs_optim.step(closure)

    return linear_clf

def train_ssl(simclr_ft,
              optimizer,
              n_epochs,
              device,
              loader_train,
              loader_val=None,
              dataset_name='cifar10'
              ):
    """
    Fine tune a pretrained SimCLR model with a linear head.
    Args:
     simclr_ft: a SimCLRFineTune model.
     optimizer: optimizer.
     n_epochs (int): number of epochs to tune the model for.
     device (torch.device): 'cuda' or 'cpu'.
     loader_train: dataloader that contains the training dataset for
                   fine tuning.
     loader_val: optional validataion loader.
     dataset_name (str): for saving the model.
    Returns: Nothing.
    """
    patience = 5
    patience_counter = 0
    best_acc = 0
    total_loss, total_acc = [], []
    optimizer.zero_grad()
    simclr_ft = simclr_ft.to(device=device)

    loss_fn = nn.CrossEntropyLoss()
    for e in range(n_epochs):
        loss_per_ep, acc_per_ep = [], []
        simclr_ft.train()
        for i, (img, targets) in enumerate(loader_train):
            img = img.to(device=device, dtype=torch.float32)
            targets = targets.to(device=device, dtype=torch.long)
            score = simclr_ft(img)
            loss = loss_fn(score, targets)

            loss.backward()
            optimizer.step()
            optimizer.zero_grad()

            # Calculate accuracy
            _, pred = score.max(1)
            correct = sum([pred[i] == targets[i] for i in range(targets.shape[0])])
            accuracy = 100. * correct / targets.shape[0]
            loss_per_ep.append(loss.item())
            acc_per_ep.append(accuracy.item())

        total_loss.append(sum(loss_per_ep) / len(loss_per_ep))
        total_acc.append(sum(acc_per_ep) / len(acc_per_ep))
        # Print training loss and accuracy every epoch
        print('epoch {}: | Train Loss: {:.3f} | Train Top 1 Accuracy: {:.3f}%'.format((e + 1), loss.item(), accuracy))
        if loader_val is not None:
            val_loss, val_acc = test_ssl(simclr_ft, device, loader_val, return_loss_accuracy=True)
            if val_acc > best_acc:
                best_acc = val_acc
                patience_counter = 0
                print("Found a better model, saving...")
                torch.save(simclr_ft.state_dict(),
                           configs["colab_path"] + "fine_tune_{}.pth".format(
                               dataset_name))
            else:
                patience_counter += 1
                if patience_counter == patience:
                    print("Early stopping ... ")
                    plot_loss_acc(loss=total_loss, accuracy=total_acc)
                    return
    print(total_acc)
    plot_loss_acc(loss=total_loss, accuracy=total_acc)

def test_ssl(simclr_ft,
             device,
             loader_test,
             return_loss_accuracy=False):
    """
    Test fine tuned simclr model.
    Args:
     simclr_ft: a SimCLRFineTune model.
     device (torch.device): 'cuda' or 'cpu'.
     loader_test: dataloader that contains data for testing.
    Returns:
     (Optionally) Loss and accuracy (Top1).
    """
    simclr_ft = simclr_ft.to(device=device)
    simclr_ft.eval()
    loss_fn = nn.CrossEntropyLoss()
    losses = []
    total_correct = 0
    total_sample = 0
    t = tqdm(enumerate(loader_test), desc='Testing the classification model ...')
    with torch.no_grad():
        for b, (img, targets) in t:
            total_sample += targets.shape[0]
            img = img.to(device=device, dtype=torch.float32)
            targets = targets.to(device=device, dtype=torch.long)
            scores = simclr_ft(img)
            loss = loss_fn(scores, targets)
            losses.append(loss)
            _, pred = scores.max(1)
            correct = sum([pred[i] == targets[i] for i in range(targets.shape[0])])
            total_correct += correct
    acc = 100. * total_correct / total_sample
    print('Got %d / %d correct (%.2f)' % (total_correct, total_sample, acc))

    if return_loss_accuracy:
        return sum(losses) / len(losses), acc


import time

from telethon.events import NewMessage
from telethon.tl.custom import Dialog
from telethon.tl.types import Channel, Chat, User

from . import *

@bot.on(mew_cmd(pattern="stats$"))
@bot.on(sudo_cmd(pattern="stats$", allow_sudo=True))
async def stats(
    event: NewMessage.Event,
) -> None:  # pylint: disable = R0912, R0914, R0915
    if event.fwd_from:
        return
    Meow = await edit_or_reply(event, "`cσℓℓεcтιηg sтαтs...`")
    start_time = time.time()
    private_chats = 0
    bots = 0
    groups = 0
    broadcast_channels = 0
    admin_in_groups = 0
    creator_in_groups = 0
    admin_in_broadcast_channels = 0
    creator_in_channels = 0
    unread_mentions = 0
    unread = 0
    dialog: Dialog
    async for dialog in event.client.iter_dialogs():
        entity = dialog.entity
        if isinstance(entity, Channel):
            # participants_count = (await event.get_participants(dialog,
            # limit=0)).total
            if entity.broadcast:
                broadcast_channels += 1
                if entity.creator or entity.admin_rights:
                    admin_in_broadcast_channels += 1
                if entity.creator:
                    creator_in_channels += 1
            elif entity.megagroup:
                groups += 1
                # if participants_count > largest_group_member_count:
                #     largest_group_member_count = participants_count
                if entity.creator or entity.admin_rights:
                    # if participants_count > largest_group_with_admin:
                    #     largest_group_with_admin = participants_count
                    admin_in_groups += 1
                if entity.creator:
                    creator_in_groups += 1
        elif isinstance(entity, User):
            private_chats += 1
            if entity.bot:
                bots += 1
        elif isinstance(entity, Chat):
            groups += 1
            if entity.creator or entity.admin_rights:
                admin_in_groups += 1
            if entity.creator:
                creator_in_groups += 1
        unread_mentions += dialog.unread_mentions_count
        unread += dialog.unread_count
    stop_time = time.time() - start_time
    full_name = inline_mention(await event.client.get_me())
    response = f"🌠**ѕtαtѕ fσr  {full_name}**🌠\n\n"
    response += f"☪ **prívαtє chαtѕ:** {private_chats} \n"
    response += f"🔻   `υsεяs: {private_chats - bots}` \n"
    response += f"🔺   `вσтs: {bots}` \n"
    response += f"💟 **grσupѕ:** {groups} \n"
    response += f"💟 **chαnnєlѕ:** {broadcast_channels} \n"
    response += f"☪  **αdmínѕ ín grσupѕ:** {admin_in_groups} \n"
    response += f"🔻   `cяεαтσя: {creator_in_groups}` \n"
    response += f"🔺   `α∂мιη яιgнтs: {admin_in_groups - creator_in_groups}` \n"
    response += f"💟  **αdmín ín chαnnєlѕ:** {admin_in_broadcast_channels} \n"
    response += f"🔻   `cяεαтεя: {creator_in_channels}` \n"
    response += (
        f"🔹   `αdmín ríghtѕ: {admin_in_broadcast_channels - creator_in_channels}` \n"
    )
    response += f"☪ **υηяεα∂:** {unread} \n"
    response += f"☪ **υηяεα∂ мεηтισηs:** {unread_mentions} \n\n"
    response += f"💟   __It Took:__ {stop_time:.02f}s \n"
    response += f"✨ **ρσωεяε∂ вү ** {mew_channel} 🌠"
    await Meow.edit(response)

def make_mention(user):
    if user.username:
        return f"@{user.username}"
    return inline_mention(user)

def inline_mention(user):
    full_name = user_full_name(user) or "Meow"
    return f"[{full_name}](tg://user?id={user.id})"

def user_full_name(user):
    names = [user.first_name, user.last_name]
    names = [i for i in list(names) if i]
    return " ".join(names)

CmdHelp("stats").add_command(
    "stats", None, "Shows you the count of your groups, channels, private chats, etc."
).add_info("Statistics Of Account").add_warning("✅ Harmless Module.").add()


import yaml
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer
from dataset import *
from sklearn.metrics import accuracy_score, precision_recall_fscore_support, roc_auc_score

class HIVTrainer():
    def __init__(self, train_data, model_name='Rostlab/prot_bert', training_args_path='training_args.yaml'):

        def model_init():
            return AutoModelForSequenceClassification.from_pretrained(model_name)

        with open(training_args_path, 'r') as f:
            yaml_file = yaml.load(f, Loader=yaml.FullLoader)

        training_args = TrainingArguments(**yaml_file)

        self.model_name = model_name
        self.train_dataset = self.build_dataset(train_data, split='train', tokenizer_name=model_name)
        self.val_dataset = self.build_dataset(train_data, split='test', tokenizer_name=model_name)

        self.trainer = Trainer(
            model_init=model_init,                # the instantiated ðŸ¤— Transformers model to be trained
            args=training_args,                   # training arguments, defined above
            train_dataset=self.train_dataset,          # training dataset
            eval_dataset=self.val_dataset,             # evaluation dataset
            compute_metrics = compute_metrics,    # evaluation metrics
        )

    def train(self):
        self.trainer.train()

    def test(self, test_data):
        test_dataset = self.build_dataset(test_data, split='test', tokenizer_name=self.model_name)
        predictions, label_ids, metrics = self.trainer.predict(test_dataset)
        return predictions, label_ids, metrics

    def build_dataset(self, *args, **kwargs):
        return HIVDataset(*args, **kwargs)

class HIVTrainer_Albert(HIVTrainer):
    def build_dataset(self, *args, **kwargs):
        return HIVDataset_Albert(*args, **kwargs)

def compute_metrics(pred):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='binary')
    acc = accuracy_score(labels, preds)
    auc = roc_auc_score(labels, preds)
    return {
        'accuracy': acc,
        'f1': f1,
        'precision': precision,
        'recall': recall,
        'auroc': auc
    }