# -*- coding: utf-8 -*-
'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2019, Caleb Bell <Caleb.Andrew.Bell@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.'''

from numpy.testing import assert_allclose
import pytest
import numpy as np

from thermo import *
from fluids.numerics import *
from math import *
import json
import os
from fluids.numerics import assert_close, assert_close1d, assert_close2d, assert_close3d

def check_np_output_activity(model, modelnp, modelnp2):
    # model is flat, scalar, list-based model
    # modelnp is numba model
    # modelnp2 is created from the numba model with to_T_xs at a different composition

    scalar_attrs = ['d3GE_dT3', 'd2GE_dT2', 'GE', 'dGE_dT']
    for attr in scalar_attrs:
        if hasattr(model, attr):
#            print(attr)
            assert_close(getattr(model, attr)(), getattr(modelnp, attr)(), rtol=2e-13)
            assert_close(getattr(modelnp2, attr)(), getattr(modelnp, attr)(), rtol=2e-13)
            assert type(getattr(model, attr)()) is float
    #        assert type(getattr(modelnp, attr)()) is float
    #        assert type(getattr(modelnp2, attr)()) is float

    vec_attrs = ['dGE_dxs', 'gammas', '_gammas_dGE_dxs',
                 'd2GE_dTdxs', 'dHE_dxs', 'gammas_infinite_dilution', 'dHE_dns',
                'dnHE_dns', 'dSE_dxs', 'dSE_dns', 'dnSE_dns', 'dGE_dns', 'dnGE_dns', 'd2GE_dTdns',
                'd2nGE_dTdns', 'dgammas_dT']

    for attr in vec_attrs:
#        print(attr)
        assert_close1d(getattr(model, attr)(), getattr(modelnp, attr)(), rtol=2e-13)
        assert_close1d(getattr(modelnp2, attr)(), getattr(modelnp, attr)(), rtol=2e-13)
        assert type(getattr(model, attr)()) is list
        assert type(getattr(modelnp, attr)()) is np.ndarray
        assert type(getattr(modelnp2, attr)()) is np.ndarray

    mat_attrs = ['d2GE_dxixjs', 'd2nGE_dninjs', 'dgammas_dns']
    for attr in mat_attrs:
#        print(attr)
        assert_close2d(getattr(model, attr)(), getattr(modelnp, attr)(), rtol=1e-12)
        assert_close2d(getattr(modelnp2, attr)(), getattr(modelnp, attr)(), rtol=1e-12)
        assert type(getattr(model, attr)()) is list
        assert type(getattr(modelnp, attr)()) is np.ndarray
        assert type(getattr(modelnp2, attr)()) is np.ndarray

    attrs_3d = ['d3GE_dxixjxks']
    for attr in attrs_3d:
        if hasattr(model, attr):
#            print(attr)
            # some models do not have this implemented
            assert_close3d(getattr(model, attr)(), getattr(modelnp, attr)(), rtol=1e-13)
            assert_close3d(getattr(modelnp2, attr)(), getattr(modelnp, attr)(), rtol=1e-13)
            assert type(getattr(model, attr)()) is list
            assert type(getattr(modelnp, attr)()) is np.ndarray
            assert type(getattr(modelnp2, attr)()) is np.ndarray

def plot_unsupported(reason, color='r'):
    '''Helper function - draw a plot with an `x` over it displaying a message
    why that plot is not supported.
    '''
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()

    xlims = ax.get_xlim()
    ylims = ax.get_ylim()
    ax.plot([0, 1], [0, 1], lw=5, c=color)
    ax.plot([0, 1], [1, 0], lw=5, c=color)

    ax.text(.5, .5, reason, ha='center', va='center', bbox=dict(fc='white'))
    return fig

def mark_plot_unsupported(plot_fig, reason, color='r'):
    ax = plot_fig.axes[0]
    xlims = ax.get_xlim()
    ylims = ax.get_ylim()
    xmid = 10**(0.5*(log10(xlims[0]) + log10(xlims[1])))
    ymid = 10**(0.5*(log10(ylims[0]) + log10(ylims[1])))
    ax.text(xmid, ymid, reason, ha='center', va='center', bbox=dict(fc='white'))
    color = 'r'
    ax.plot(xlims, ylims, lw=5, c=color)
    ax.plot(xlims, ylims[::-1], lw=5, c=color)

default_attrs = ('phase', 'Hm', 'Sm', 'Gm', 'xs', 'ys', 'V_over_F', 'T', 'P')

def flash_rounding(x):
    if isinstance(x, float):
        return float('%.10e' %(x))
    return x

def pkg_tabular_data_TP(IDs, pkg_ID, zs, P_pts=50, T_pts=50, T_min=None,
                        T_max=None, P_min=None, P_max=None, attrs=default_attrs):
    pkg = PropertyPackageConstants(IDs, pkg_ID)

    if T_min is None:
        T_min = min(i for i in pkg.Tms if i is not None)
    if T_max is None:
        T_max = max(i for i in pkg.Tcs if i is not None)*2

    if P_min is None:
        P_min = 100
    if P_max is None:
        P_max = max(i for i in pkg.Pcs if i is not None)*2

    Ts = linspace(T_min, T_max, T_pts)
    Ps = logspace(log10(P_min), log10(P_max), P_pts)
    data = []
    for T in Ts:
        data_row = []
        for P in Ps:
            try:
                pkg.pkg.flash(T=T, P=P, zs=zs)
                row = tuple(flash_rounding(getattr(pkg.pkg, s)) for s in attrs)
            except Exception as e:
                row = tuple(None for s in attrs)
                print(e, T, P, IDs, pkg_ID, zs)
            data_row.append(row)
        data.append(data_row)

    metadata = {'spec':('T', 'P'), 'pkg': pkg_ID, 'zs': zs, 'IDs': IDs,
                'T_min': T_min, 'T_max': T_max, 'P_min': P_min, 'P_max': P_max,
                'Ts': Ts, 'Ps': Ps}

    return Ts, Ps, data, metadata

def pkg_tabular_data_TVF(IDs, pkg_ID, zs, VF_pts=50, T_pts=50, T_min=None,
                         T_max=None, VF_min=None, VF_max=None, attrs=default_attrs):
    pkg = PropertyPackageConstants(IDs, pkg_ID)

    if T_min is None:
        T_min = min(i for i in pkg.Tms if i is not None)
    if T_max is None:
        if pkg.pkg.N == 1:
            T_max = pkg.Tcs[0]
        else:
            T_max = max(i for i in pkg.Tcs if i is not None)*2

    if VF_min is None:
        VF_min = 0.0
    if VF_max is None:
        VF_max = 1.0

    Ts = linspace(T_min, T_max, T_pts)
    VFs = linspace(VF_min, VF_max, VF_pts)
    data = []
    for T in Ts:
        data_row = []
        for VF in VFs:
#            print(T, VF)
            try:
                pkg.pkg.flash(T=T, VF=VF, zs=zs)
                row = tuple(flash_rounding(getattr(pkg.pkg, s)) for s in attrs)
            except Exception as e:
                row = tuple(None for s in attrs)
                print(e, T, VF, IDs, pkg_ID, zs)
            data_row.append(row)
        data.append(data_row)

    metadata = {'spec':('T', 'VF'), 'pkg': pkg_ID, 'zs': zs, 'IDs': IDs,
                'T_min': T_min, 'T_max': T_max, 'VF_min': VF_min, 'VF_max': VF_max,
                'Ts': Ts, 'VFs': VFs}

    return Ts, VFs, data, metadata

def pkg_tabular_data_PVF(IDs, pkg_ID, zs, VF_pts=50, P_pts=50, P_min=None,
                         P_max=None, VF_min=None, VF_max=None, attrs=default_attrs):
    pkg = PropertyPackageConstants(IDs, pkg_ID)

    if P_min is None:
        P_min = 100.0
    if P_max is None:
        if pkg.pkg.N == 1:
            P_max = pkg.Pcs[0]
        else:
            P_max = max(i for i in pkg.Pcs if i is not None)*2

    if VF_min is None:
        VF_min = 0.0
    if VF_max is None:
        VF_max = 1.0

    Ps = linspace(P_min, P_max, P_pts)
    VFs = linspace(VF_min, VF_max, VF_pts)
    data = []
    for P in Ps:
        data_row = []
        for VF in VFs:
#            print(P, VF)
            try:
                pkg.pkg.flash(P=P, VF=VF, zs=zs)
                row = tuple(flash_rounding(getattr(pkg.pkg, s)) for s in attrs)
            except Exception as e:
                row = tuple(None for s in attrs)
                print(e, P, VF, IDs, pkg_ID, zs)
            data_row.append(row)
        data.append(data_row)

    metadata = {'spec':('P', 'VF'), 'pkg': pkg_ID, 'zs': zs, 'IDs': IDs,
                'P_min': P_min, 'P_max': P_max, 'VF_min': VF_min, 'VF_max': VF_max,
                'Ps': Ps, 'VFs': VFs}

    return Ps, VFs, data, metadata

def pkg_tabular_data_PH(IDs, pkg_ID, zs, P_pts=50, H_pts=50, H_min=None,
                        H_max=None, P_min=None, P_max=None, attrs=default_attrs):
    pkg = PropertyPackageConstants(IDs, pkg_ID).pkg

    if P_min is None:
        P_min = 100
    if P_max is None:
        P_max = max(i for i in pkg.Pcs if i is not None)*2

    if H_min is None or H_max is None:
        T_min = min(i for i in pkg.Tms if i is not None)
        T_max = max(i for i in pkg.Tcs if i is not None)*2

        Hm_range = []
        for P in (P_min, P_max):
            for T in (T_min, T_max):
                pkg.flash(T=T, P=P, zs=zs)
                Hm_range.append(pkg.Hm)

        if H_min is None:
            H_min = min(Hm_range)
        if H_max is None:
            H_max = max(Hm_range)

    Hs = linspace(H_min, H_max, H_pts)
    Ps = logspace(log10(P_min), log10(P_max), P_pts)
    data = []
    for H in Hs:
        data_row = []
        for P in Ps:
#            print(P, H, IDs, zs)
            pkg.flash_caloric(Hm=H, P=P, zs=zs)
            row = tuple(flash_rounding(getattr(pkg, s)) for s in attrs)
            data_row.append(row)
        data.append(data_row)

    metadata = {'spec':('P', 'H'), 'pkg': pkg_ID, 'zs': zs, 'IDs': IDs,
                'H_min': H_min, 'H_max': H_max, 'P_min': P_min, 'P_max': P_max,
                'Hs': Hs, 'Ps': Ps}

    return Hs, Ps, data, metadata

def pkg_tabular_data_PS(IDs, pkg_ID, zs, P_pts=50, S_pts=50, S_min=None,
                        S_max=None, P_min=None, P_max=None, attrs=default_attrs):
    pkg = PropertyPackageConstants(IDs, pkg_ID).pkg

    if P_min is None:
        P_min = 100
    if P_max is None:
        P_max = max(i for i in pkg.Pcs if i is not None)*2

    if S_min is None or S_max is None:
        T_min = min(i for i in pkg.Tms if i is not None)
        T_max = max(i for i in pkg.Tcs if i is not None)*2

        Sm_range = []
        for P in (P_min, P_max):
            for T in (T_min, T_max):
                pkg.flash(T=T, P=P, zs=zs)
                Sm_range.append(pkg.Sm)

        if S_min is None:
            S_min = min(Sm_range)
        if S_max is None:
            S_max = max(Sm_range)

    Ss = linspace(S_min, S_max, S_pts)
    Ps = logspace(log10(P_min), log10(P_max), P_pts)
    data = []
    for S in Ss:
        data_row = []
        for P in Ps:
#            print(P, S, IDs, zs)
            pkg.flash_caloric(Sm=S, P=P, zs=zs)
            row = tuple(flash_rounding(getattr(pkg, s)) for s in attrs)
            data_row.append(row)
        data.append(data_row)

    metadata = {'spec':('P', 'S'), 'pkg': pkg_ID, 'zs': zs, 'IDs': IDs,
                'S_min': S_min, 'S_max': S_max, 'P_min': P_min, 'P_max': P_max,
                'Ss': Ss, 'Ps': Ps}

    return Ss, Ps, data, metadata

def save_tabular_data_as_json(specs0, specs1, metadata, data, attrs, path):
    r'''Saves tabular data from a property package to a json file for testing,
    plotting, and storage.

    Parameters
    ----------
    specs0 : float
        List of values of the first spec; i.e. if the first spec was T,
        a list of temperatures
    specs1 : float
        List of values of the first spec; i.e. if the second spec was P,
        a list of pressures
    metadata : dict
        A dictionary of json-serializeable metadata from the property
        package; the specifications, range of values, compositions, and
        compounds are good values to include
    data : list[list[tuple]]
        A list of lists of tuples containing the data values specified in
        `attrs`
    attrs : tuple[str]
        The attributes stored from the property package
    path : str
        The path for the json-formatted data to be stored

    Notes
    -----

    Examples
    --------

    '''
    data_json = []

    for row, spec0 in zip(data, specs0):
        new_rows = []

        for i, spec1 in enumerate(specs1):
            new_rows.append({k: v for k, v in zip(attrs, row[i])})
        data_json.append(new_rows)

    metadata['attrs'] = attrs
    json_result = {'metadata': metadata, 'tabular_data': data_json}

    fp = open(path, 'w')
    json.dump(json_result, fp, sort_keys=True, indent=2)
    fp.close()
    return True

tabular_data_functions = {'TP': pkg_tabular_data_TP,
                          'TVF': pkg_tabular_data_TVF,
                          'PVF': pkg_tabular_data_PVF,
                          'PH': pkg_tabular_data_PH,
                          'PS': pkg_tabular_data_PS}


#!/usr/bin/env python3

# Arguments:
# -h: help, -v: verbose mode -l testing level, -m mpi-tasks, -d sw4-exe-dir -t omp-threads

import os, sys, argparse, subprocess, time
#os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE"

#------------------------------------------------
def compare_one_line(base_file_name, test_file_name, errTol, absErrLimit, lineNum, verbose):

    success = True

    base_file = open(base_file_name)
    test_file = open(test_file_name)
    base_data = base_file.readlines() #reads all lines in the file
    test_data = test_file.readlines()

    # tmp
    #print('base_data file:', base_data)
    #print('test_data file:', test_data)

    base_file.close()
    test_file.close()
    if len(base_data) != len(test_data):
        print("WARNING: " + base_file_name + " and " + test_file_name + " are different lengths.")

    # for twilight tests, compare the 3 numbers on the last line of each file
    base_line = base_data[lineNum]
    test_line = test_data[lineNum]
    #print('base_line=', base_line)
    #print('test_line=', test_line)

    base_data = base_line.split() #base_data is a list of strings
    test_data = test_line.split()
    #print('base_data=', base_data)
    #print('test_data=', test_data)

    base_num = [float(x) for x in base_data] #base_num is a list of floats
    test_num = [float(x) for x in test_data]
    #print('base_num=', base_num);
    #print('test_num=', test_num);

    try:
        for jj in range(len(base_data)):
            t0 = test_num[jj]
            b0 = base_num[jj]
            re0 = 0
            if abs(b0) > absErrLimit:
                re0 = abs(b0-t0)/abs(b0)
            else:
                re0 = abs(b0-t0)

            if verbose or re0 > errTol:
                print('INFO: compare_one_line: col=', jj, 'test=', t0, 'base=', b0, 'err=', re0);

            if re0 > errTol:
                print('ERROR: compare_one_line: err=', re0, '> tolerance=', errTol)
                success = False
            # end if
        # end for
    except:
        success = False

    base_file.close()
    test_file.close()

    return success

#------------------------------------------------
def compare_energy(test_file_name, errTol, verbose):

    success = True;

    f = open(test_file_name)

    Lnum=0;
    for line in f:
        Lnum = Lnum+1;
        if Lnum <= 3:
            thisEnergy = float(line);
            thirdEnergy = thisEnergy;
            if verbose and Lnum == 3:
                print('INFO: compare_energy: Ref. Energy =', thirdEnergy);

        else:
            prevEnergy = thisEnergy;
            thisEnergy = float(line);
# relative change from previous energy
            diff0 = (thisEnergy - prevEnergy)/thirdEnergy;

            if diff0 > errTol:
                print('ERROR: compare_energy: line =', Lnum, 'prev =', prevEnergy, 'this =', thisEnergy, 'rel diff =', diff0, '> tolerance=', errTol);
                success = False
                break
            # end if
        # end if
    # end for
    if (Lnum < 4):
        print("ERROR: compare_energy: Less than 4 lines in the energy log!");
        success=False;
    elif verbose:
        print('INFO: compare_energy: line =', Lnum, 'prev =', prevEnergy, 'this =', thisEnergy, 'rel diff =', diff0);

    f.close()

    return success

#------------------------------------------------
def compare_misfit(base_file_name, test_file_name, errTol, lineNum, verbose):

    success = True

    base_file = open(base_file_name)
    test_file = open(test_file_name)

    if not base_file:
        print("ERROR: compare_misfit failed to open reference file!", base_file_name);
        return False
    elif not test_file:
        print("ERROR: compare_misfit failed to open result file!", test_file_name);
        return False

    base_data = base_file.readlines() #reads all lines in the file
    test_data = test_file.readlines()

    base_line = base_data[lineNum]
    test_line = test_data[lineNum]

    base_misfit = float(base_line.split('=')[1])
    test_misfit = float(test_line.split('=')[1])

    if abs(base_misfit-test_misfit) > errTol:
        success = False
        if verbose:
            print("INFO: mistfit difference is", abs(base_misfit-test_misfit));

    base_file.close()
    test_file.close()

    return success

#------------------------------------------------
def compare_dfm_hdf5(fname_res, fname_ref, errTol, verbose):
    if 'h5py' not in sys.modules:
        print("This test requires h5py")
        sys.exit(-1)
    import h5py, numpy

    success = True;

    f1 = h5py.File(fname_res, 'r')
    f2 = h5py.File(fname_ref, 'r')

    if not f1:
        print("ERROR: compare_dfm_hdf5 failed to open result HDF5 file!", fname_res);
        return False
    elif not f2:
        print("ERROR: compare_dfm_hdf5 failed to open reference HDF5 file!", fname_ref);
        return False
    else:
        d1 = f1['dfm'][:]
        d2 = f2['dfm'][:]

        if d1.shape != d2.shape:
            print("ERROR: compare_dfm_hdf5 result and reference dfm dataset have different shapes!", d1.shape, d2.shape);
            return False

        if numpy.max(numpy.abs(d1 - d2)) > errTol:
            success = False
            if verbose:
                print("INFO: max error is ", numpy.max(numpy.abs(d1 - d2)));

        f1.close()
        f2.close()

    return success

#------------------------------------------------
def guess_mpi_cmd(mpi_tasks, omp_threads, cpu_allocation, verbose):
    if verbose: print('os.uname=', os.uname())
    node_name = os.uname()[1]
    if verbose: print('node_name=', node_name)
    sys_name = os.uname()[0]
    if verbose: print('sys_name=', sys_name)

    if 'quartz' in node_name:
        if omp_threads<=0: omp_threads=1;
        if mpi_tasks<=0: mpi_tasks = int(36/omp_threads)
        if cpu_allocation == "":
           mpirun_cmd="srun -ppdebug -n " + str(mpi_tasks) + " -c " + str(omp_threads)
        else:
           mpirun_cmd="srun -ppdebug -A " + cpu_allocation + " -n " + str(mpi_tasks) + " -c " + str(omp_threads)
    elif 'cab' in node_name:
        if omp_threads<=0: omp_threads=2;
        if mpi_tasks<=0: mpi_tasks = int(16/omp_threads)
        mpirun_cmd="srun -ppdebug -n " + str(mpi_tasks) + " -c " + str(omp_threads)
    elif 'nid' in node_name: # the cori knl nodes are called nid
        if omp_threads<=0: omp_threads=4;
        if mpi_tasks<=0: mpi_tasks = int(32/omp_threads) # use 64 hardware cores per node
        sw_threads = omp_threads # Cori uses hyperthreading by default
        mpirun_cmd="srun --cpu_bind=cores -n " + str(mpi_tasks) + " -c " + str(sw_threads)
    elif 'fourier' in node_name:
        if mpi_tasks<=0: mpi_tasks = 4
        mpirun_cmd="mpirun -np " + str(mpi_tasks)
    elif 'ray' in node_name:
        if mpi_tasks<=0: mpi_tasks = 16
        mpirun_cmd="mpirun -gpu -np " + str(mpi_tasks)+" mpibind"
    elif 'lassen' in node_name:
        if mpi_tasks<=0: mpi_tasks = 16
        mpirun_cmd="lrun -T40 "
    elif 'sierra' in node_name:
        if mpi_tasks<=0: mpi_tasks = 16
        mpirun_cmd="lrun -T16 -p" + str(mpi_tasks)
    elif 'batch' in node_name: # for summit
        if omp_threads<=0: omp_threads=7;
        if mpi_tasks<=0: mpi_tasks = 6
        mpirun_cmd="jsrun -a1 -c7 -r6 -l CPU-CPU -d packed -b packed:7 -n " + str(mpi_tasks)
        # mpirun_cmd="jsrun -a1 -c7 -g1 -l CPU-CPU -d packed -b packed:7 -M -gpu -n " + str(mpi_tasks)
    # add more machine names here
    elif 'Linux' in sys_name:
        if mpi_tasks<=0: mpi_tasks = 1
        mpirun_cmd="mpirun -np " + str(mpi_tasks)
    else:
        #default mpi command
        if mpi_tasks<=0: mpi_tasks = 1
        mpirun_cmd="mpirun -np " + str(mpi_tasks)

    return mpirun_cmd

#------------------------------------------------
def main_test(sw4_exe_dir="optimize", testing_level=0, mpi_tasks=0, omp_threads=0, cpu_allocation="", usehdf5=False, verbose=False):
    assert sys.version_info >= (3,5) # named tuples in Python version >=3.3
    sep = '/'
    pytest_dir = os.getcwd()
    pytest_dir_list = pytest_dir.split(sep)
    sw4_base_list = pytest_dir_list[:-1] # discard the last sub-directory (pytest)

    sw4_base_dir = sep.join(sw4_base_list)
    optimize_dir =  sw4_base_dir + sep + sw4_exe_dir
    reference_dir = pytest_dir + '/reference'

    # make sure the directories are there
    if not os.path.isdir(sw4_base_dir):
        print("ERROR: directory", sw4_base_dir, "does not exists")
        return False
    if not os.path.isdir(optimize_dir):
        print("ERROR: directory", optimize_dir, "does not exists (HINT: use -d 'sw4_exe_dir')")
        return False
    if not os.path.isdir(reference_dir):
        print("ERROR: directory", reference_dir, "does not exists")
        return False

    if verbose: print('pytest_dir =', pytest_dir)
    if verbose: print('sw4_base_dir =', sw4_base_dir)
    if verbose: print('optimize_dir =', optimize_dir)
    if verbose: print('reference_dir =', reference_dir)

    sw4_exe = optimize_dir + '/sw4mopt'
    #print('sw4-exe = ', sw4_exe)

    # make sure sw4 is present in the optimize dir
    if not os.path.isfile(sw4_exe):
        print("ERROR: the file", sw4_exe, "does not exists (DID YOU FORGET TO BUILD SW4MOPT?)")
        return False

    # guess the mpi run command from the uname info
    mpirun_cmd=guess_mpi_cmd(mpi_tasks, omp_threads, cpu_allocation, verbose)

    sw4_mpi_run = mpirun_cmd + ' ' + sw4_exe
    if (omp_threads>0):
        os.putenv("OMP_NUM_THREADS", str(omp_threads))

    if verbose: print('sw4_mpi_run = ', sw4_mpi_run)

    num_test=0
    num_pass=0
    num_fail=0

    all_dirs    = ['gradtest','hesstest','misfitcurve','onepoint','gaussian', 'gaussian']
    all_cases   = ['grad','hesstest','misfit1d','inv','gaussian-lbfgs', 'gaussian-nlcg']
    all_results = ['GradientTest.txt', 'Hessian.txt', 'Misfit1d.txt', 'convergence.log',
                   'gaussian-lbfgs.out', 'gaussian-nlcg.out']
    all_abslim  = [1e-10, 1e-20, 1e-10, 1e-10, 1e-10, 1e-10]
    num_meshes  = [1, 1, 1, 1, 1, 1] # default number of meshes for level 0

    print("Running all tests for level", testing_level, "...")

    # Last two tests are HDF5 ones, only run when explicitly enabled
    if usehdf5:
        qend = len(all_dirs)
    else:
        qend = len(all_dirs) - 2

# run all tests
    for qq in range(0, qend):
        test_dir = all_dirs[qq]
        base_case = all_cases[qq]
        result_file = all_results[qq]

        #make a local test directory
        if not os.path.exists(test_dir):
            os.mkdir(test_dir)

        os.chdir(test_dir) # change to the new local directory
        print("test_dir:", test_dir)

        # put all test cases in a list and add a for loop for each test_dir
        for ii in range(num_meshes[qq]):
            num_test = num_test+1

            case_file = base_case
            test_case = case_file + '.in'
            if verbose:
                print('Starting test #', num_test, 'in directory:', test_dir, 'with input file:', test_case)

            sw4_input_file = reference_dir + sep + test_dir + sep + test_case
            if not os.path.isfile(sw4_input_file):
                print("ERROR: the input file", sw4_input_file, "does not exists")
                return False
            #print('sw4_input_file = ', sw4_input_file)

#            sw4_stdout_file = case_file + '.out'

            local_dir = pytest_dir + sep + test_dir
            #print('local_dir = ', local_dir)
            # pipe stdout and stderr to a temporary file
            run_cmd = mpirun_cmd.split() + [
                sw4_exe,
                sw4_input_file
            ]

            sw4_stdout_file = open(case_file + '.out', 'wt')
            sw4_stderr_file = open(case_file + '.err', 'wt')

            # pipe stdout and stderr to a temporary file
#            run_cmd = sw4_mpi_run + ' ' + sw4_input_file + ' >& ' + sw4_stdout_file

            if result_file == 'gaussian-lbfgs.out' or result_file == 'gaussian-nlcg.out':
                if not os.path.isfile("./data/obs5.h5"):
                    os.symlink(reference_dir + sep + test_dir + sep + "data", "./data")
                if not os.path.isfile("./homo.rfile"):
                    os.symlink(reference_dir + sep + test_dir + sep + "homo.rfile", "./homo.rfile")

            # run sw4mopt
            run_dir = os.getcwd()
            start=time.time()
            status = subprocess.run(
                run_cmd,
                stdout=sw4_stdout_file,
                stderr=sw4_stderr_file
            )
            end = time.time()-start
            sw4_stdout_file.close()
            sw4_stderr_file.close()
            if verbose: print(sw4_input_file, "Total time",end)

            if status.returncode!=0:
                print('ERROR: Test', test_case, ': sw4mopt returned non-zero exit status=', status.returncode, 'aborting test')
                print('run_cmd=', run_cmd)
                print("DID YOU USE THE CORRECT SW4MOPT EXECUTABLE? (SPECIFY DIRECTORY WITH -d OPTION)")
                return False # bail out

            ref_result = reference_dir + sep + test_dir + sep + 'run1' + sep + result_file
            #print('Test #', num_test, 'output dirs: local case_file =', case_file, 'ref_result =', ref_result)

            if result_file == 'energy.log':
                success = compare_energy('run1' + sep + result_file, 1e-10, verbose)
            elif result_file == 'gaussian-lbfgs.out':
                ref_h5 = reference_dir + sep + test_dir + sep + 'run1' + sep + 'lbfgs.dfm.h5'
                # print('ref_h5:', ref_h5)
                my_h5 = './lbfgs_output/dfm.h5'
                # print('my file:', my_h5)
                success = compare_dfm_hdf5(my_h5, ref_h5, all_abslim[qq], verbose)
                if success:
                    # compare the mistfit value
                    ref_result = reference_dir + sep + test_dir + sep + result_file
                    success = compare_misfit(ref_result, result_file, 1e-5, -4, verbose)
            elif result_file == 'gaussian-nlcg.out':
                ref_h5 = reference_dir + sep + test_dir + sep + 'run1' + sep + 'nlcg.dfm.h5'
                my_h5 = './nlcg_output/dfm.h5'
                success = compare_dfm_hdf5(my_h5, ref_h5, all_abslim[qq], verbose)
                if success:
                    # compare the mistfit value
                    ref_result = reference_dir + sep + test_dir + sep + result_file
                    success = compare_misfit(ref_result, result_file, 1e-5, -4, verbose)
            else:
                # compare output with reference result (always compare the last line)
                success = compare_one_line(ref_result , 'run1' + sep + result_file, 1e-5, all_abslim[qq], -1, verbose)
                if success and 'attenuation' in test_dir:
                    # also compare the 3rd last line in the files
                    success = compare_one_line(ref_result , 'run1' + sep + result_file, 1e-5, all_abslim[qq], -3, verbose)
            if success:
                print('Test #', num_test, "Input file:", test_case, 'PASSED')
                num_pass += 1
            else:
                print('Test #', num_test, "Input file:", test_case, 'FAILED')
                num_fail += 1

        # end for qq in all_dirs[qq]

        # cleanup tmp dir
        listOfTmpFiles = os.listdir(path='tmp');
#        print('listOfTmpFiles:', listOfTmpFiles);
        for fname in listOfTmpFiles:
            fullName = os.path.join("tmp",fname);
            print('fileName = ', fullName);
            os.remove(fullName); # remove each file in the tmp dir
        #end for
        os.rmdir("tmp");
        if verbose:
            print('Done removing tmp dir');

        os.chdir('..') # change back to the parent directory

    # end for all cases in the test_dir
    print('Out of', num_test, 'tests,', num_fail, 'failed and ', num_pass, 'passed')
    # normal termination
    return True

#------------------------------------------------
if __name__ == "__main__":
    assert sys.version_info >= (3,5) # named tuples in Python version >=3.3

    # default arguments
    testing_level=0
    verbose=False
    mpi_tasks=0 # machine dependent default
    omp_threads=0 #no threading by default
    cpu_allocation=""
    usehdf5=False

    parser=argparse.ArgumentParser()
    parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
    parser.add_argument("-l", "--level", type=int, choices=[0, 1, 2],
                        help="testing level")
    parser.add_argument("-m", "--mpitasks", type=int, help="number of mpi tasks")
    parser.add_argument("-t", "--ompthreads", type=int, help="number of omp threads per task")
    parser.add_argument("-d", "--sw4_exe_dir", help="name of directory for sw4 executable", default="optimize")
    parser.add_argument("-A","--cpu_allocation", help="name of cpu bank/allocation",default="")
    parser.add_argument("-u", "--usehdf5", help="run HDF5 tests", action="store_true")
    args = parser.parse_args()
    if args.verbose:
        #print("verbose mode enabled")
        verbose=True
    if args.level:
        #print("Testing level specified=", args.level)
        testing_level=args.level
    if args.mpitasks:
        #print("MPI-tasks specified=", args.mpitasks)
        if args.mpitasks > 0: mpi_tasks=args.mpitasks
    if args.ompthreads:
        #print("OMP-threads specified=", args.ompthreads)
        if args.ompthreads > 0: omp_threads=args.ompthreads
    if args.sw4_exe_dir:
        #print("sw4_exe_dir specified=", args.sw4_exe_dir)
        sw4_exe_dir=args.sw4_exe_dir
    if args.cpu_allocation:
        #print("cpu_allocation specified=", args.cpu_allocation)
        cpu_allocation=args.cpu_allocation
    if args.verbose:
        print("HDF5 tests enabled")
        usehdf5=True

    if not main_test(sw4_exe_dir, testing_level, mpi_tasks, omp_threads, cpu_allocation, usehdf5, verbose):
        print("test_sw4 was unsuccessful")
        sys.exit(-1)


import unittest
from unittest import mock
from final_coffee_db import DataBase
from coffee_web import app

COFEE_DB = "CoffeeForMeDB.db"
fake_db = 'final_coffee_db.COFEE_DB'

def not_a_db_hit():
    print('I did not hit the db')

class Test_DB(unittest.TestCase):

    @mock.patch(fake_db)
    def test_execute_query_get(self, mock_db):
        expected_info =  [('Sugar',)]
        query = 'select bev_type from beverage_type'
        self.assertFalse(mock_db.execute_query_get(query) == expected_info, "Get method is wrong")

    @mock.patch(fake_db)
    def test_execute_query_post(self, mock_db):
        query = 'INSERT INTO sales (name, bill, salesdate) VALUES (?, ?, ?)'
        bill_info = ('John', 3.2, '2019-04-14')
        self.assertFalse(mock_db.send_bill_to_db(bill_info) == True, "Post query is wrong")

    @mock.patch(fake_db)
    def test_get_beverage_types(self, mock_db):
        expected_info =  [('Raf',)]
        print(mock_db.get_beverage_types())
        self.assertFalse(mock_db.get_beverage_types() == expected_info, "Beverage types are wrong")

    @mock.patch(fake_db)
    def test_get_additionals_types(self, mock_db):
        expected_info = [('Sugar',)]
        self.assertFalse(mock_db.get_additionals_types() == expected_info, "Additionals types are wrong")

    @mock.patch(fake_db)
    def test_get_beverage_price(self, mock_db):
        query = 'SELECT price_bev from beverage_type where bev_type = ?'
        param = ('Raf')
        self.assertFalse(mock_db.get_beverage_price(query, param) == 3, "Requesting price is wrong")

    @mock.patch(fake_db)
    def test_salessum_of_salesman(self, mock_db):
        query = 'SELECT SUM(bill) FROM sales where name = ?'
        param = ('John')
        self.assertFalse(mock_db.salessum_of_salesman(query, param) == 3, "Requesting sales sum for a salesman is wrong")

    @mock.patch('coffee_web.db')
    def test_start_page(self, mock_db_web):
        response = app.test_client().get('/')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_salesman(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/salesman')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_beverage(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/John/beverage')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_additional(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/Ann/coffee Raf')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_price(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/Ann/coffee Raf/Sugar')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_make_order(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/Ann/coffee Raf/Sugar/bill')
        self.assertEqual(response.status_code, 200)

    @mock.patch('coffee_web.db')
    def test_manager(self, mock_db_web):
        mock_db_web.side_effect = not_a_db_hit
        response = app.test_client().get('/manager')
        self.assertEqual(response.status_code, 200)

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


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Data preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Neural Net modules
import tensorflow as tf
from tensorflow.keras import Input
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import EarlyStopping

import os
import random as rn

# set seed values for reproducibility
os.environ["PYTHONHASHSEED"] = "0"
os.environ[
    "CUDA_VISIBLE_DEVICES"
] = ""  # changing "" to "0" or "-1" may solve import issues
np.random.seed(46)
rn.seed(1342)
tf.random.set_seed(62)

def finite_difference(m1, m2, y1, y2, n_x, use_simple_diff=False):
    """
    Calculate the first-order gradient between provided sample points m1 and
    m2, where each point is assumed to be a vector with one or more input
    variables x and exactly one output variable y. y1 is the value of y1 at m1,
    and y2 is the value of y at m2.

    The total gradient is calculated via chain rule assuming a multivariate
    function y(x1, x2, x3, ...). In the notation below, D/D denotes a total
    derivative and d/d denotes a partial derivative. Total derivatives are
    functions of all (x1, x2, x3, ...) whereas partial derivatives are
    functions of one input (e.g. x1) holding (x2, x3, ...) constant:

        Dy/Dx1 = (dy/dx1)(dx1/dx1) + (dy/dx2)(dx2/dx1) + (dy/dx3)(dx3/dx1) +...

    Note that (dx1/dx1) = 1. The partial derivatives dv2/dv1 are estimated
    between sample points m1 and m2 as:

        dv2/dv1 at (m1+m2)/2 = [v2 at m2 - v2 at m1]/[v1 at m2 - v1 at m1]

    The method assumes that m1 is the first point and m2 is the second point,
    and returns a vector dy_dm that is the same length as m1 and m2; m1 and m2
    must be the same length. y1 and y2 must be float or integer values.
    """

    def diff(y2, y1, x2, x1):
        """
        Calculate derivative of y w.r.t. x.
        """
        dv2_dv1 = (y2 - y1) / (x2 - x1)

        return dv2_dv1

    mid_m = [None] * n_x  # initialize dm vector, the midpoints of m1 and m2
    dy_dm = [None] * n_x  # initialize dy vector, this is dy_dm(midpoints)

    for i in range(n_x):  # for each input xi
        if use_simple_diff:
            dy_dm[i] = diff(y2, y1, m2[i], m1[i])
        else:  # use chain rule
            dy_dm[i] = sum(
                diff(y2, y1, m2[j], m1[j])
                * diff(m2[j], m1[j], m2[i], m1[i])  # dy/dxj  # dxj/dxi
                for j in range(n_x)
            )  # for each input xj

        mid_m[i] = m2[i] - m1[i]

    return mid_m, dy_dm

def predict_gradients(
    midpoints,
    gradients_midpoints,
    x,
    n_m,
    n_x,
    show_plots=True,
    optimize_training=False,
):
    """
    Train MLP regression model with data normalization on gradients at
    midpoints to predict gradients at sample point.

    Setting random_state to an integer and shuffle to False, along with the
    fixed seeds in the import section at the top of this file, will ensure
    reproducible results each time the file is run. However, calling the model
    training multiple times on the same data in the same file run will produce
    different results due to randomness in the random_state instance that is
    generated. Therefore, the training is performed for a preset list of model
    settings and the best option is selected.
    """
    # split into X_train and X_test
    # always split into X_train, X_test first THEN apply minmax scaler
    print("Normalizing data...")
    X_train, X_test, y_train, y_test = train_test_split(
        midpoints,
        gradients_midpoints,
        test_size=0.2,
        random_state=42,  # for reproducibility
        shuffle=False,
    )  # for reproducibility
    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

    # use minMax scaler
    print("Normalizing data...")
    min_max_scaler = MinMaxScaler()
    X_train = min_max_scaler.fit_transform(X_train)
    X_test = min_max_scaler.transform(X_test)

    print("Training gradient prediction model...")
    best_loss = 1e30  # insanely high value that will for sure be beaten
    best_model = None
    best_settings = None
    progress = 0

    if optimize_training:
        optimizers = ["Adam", "rmsprop"]
        activations = ["relu", "sigmoid"]
        act_outs = ["linear", "relu"]
        num_neurons = [6, 12]
        num_hidden_layers = [2, 8]
    else:
        optimizers = [
            "Adam",
        ]
        activations = [
            "relu",
        ]
        act_outs = [
            "linear",
        ]
        num_neurons = [
            6,
        ]
        num_hidden_layers = [
            2,
        ]

    for optimizer in optimizers:
        for activation in activations:
            for act_out in act_outs:
                for neuron in num_neurons:
                    for num_hidden_layer in num_hidden_layers:
                        progress += 1
                        if optimize_training:
                            print(
                                "Trying ",
                                optimizer,
                                "solver with ",
                                activation,
                                "on hidden nodes, ",
                                act_out,
                                "on output node with ",
                                neuron,
                                "neurons per node and ",
                                num_hidden_layer,
                                "hidden layers",
                            )
                        inputs = Input(
                            shape=X_train.shape[1]
                        )  # input node, layer for x1, x2, ...
                        h = Dense(neuron, activation=activation)(inputs)
                        for num in range(num_hidden_layer):
                            h = Dense(neuron, activation=activation)(h)
                        outputs = Dense(n_x, activation=act_out)(
                            h
                        )  # output node, layer for dy/dx1, dy/dx2, ...
                        model = Model(inputs=inputs, outputs=outputs)
                        # model.summary() # see what your model looks like

                        # compile the model
                        model.compile(optimizer=optimizer, loss="mse", metrics=["mae"])

                        # early stopping callback
                        es = EarlyStopping(
                            monitor="val_loss",
                            mode="min",
                            patience=50,
                            restore_best_weights=True,
                        )

                        # fit the model!
                        # attach it to a new variable called 'history' in case
                        # to look at the learning curves
                        history = model.fit(
                            X_train,
                            y_train,
                            validation_data=(X_test, y_test),
                            callbacks=[es],
                            epochs=100,
                            batch_size=50,
                            verbose=0,
                        )
                        if len(history.history["loss"]) == 100:
                            print("Successfully completed, 100 epochs run.")
                        else:
                            print(
                                "Validation loss stopped improving after ",
                                len(history.history["loss"]),
                                "epochs. Successfully completed after early stopping.",
                            )
                        print("Loss: ", sum(history.history["loss"]))
                        if optimize_training:
                            print("Progress: ", 100 * progress / 32, "%")

                        if sum(history.history["loss"]) < best_loss:
                            best_loss = sum(history.history["loss"])
                            best_model = model
                            best_history = history
                            best_settings = [
                                optimizer,
                                activation,
                                act_out,
                                neuron,
                                num_hidden_layer,
                            ]

    if optimize_training:
        print("The best settings are: ", best_settings)

    if show_plots:
        history_dict = best_history.history
        loss_values = history_dict["loss"]  # you can change this
        val_loss_values = history_dict["val_loss"]  # you can also change this
        epochs = range(1, len(loss_values) + 1)  # range of X (no. of epochs)
        plt.plot(epochs, loss_values, "bo", label="Training loss")
        plt.plot(epochs, val_loss_values, "orange", label="Validation loss")
        plt.title("Training and validation loss")
        plt.xlabel("Epochs")
        plt.ylabel("Loss")
        plt.legend()
        plt.show()

    gradients = best_model.predict(x)  # predict against original sample points

    return gradients

def generate_gradients(
    xy_data, n_x, show_plots=True, optimize_training=False, use_simple_diff=False
):
    """
    This method implements finite difference approximation and NN regression
    to estimate the first-order derivatives of a given dataset with columns
    (x1, x2, ...., xN, y1, y2, ..., yM) where N is the number of input
    variables and M is the number of output variables. The method takes an
    array of size (m, n_x + n_y) where m is the number of samples, n_x is the
    number of input variables, and n_y is the number of output variables. The
    method returns an array of size (m, n_x, n_y) where the first dimension
    spans samples, the second dimension spans gradients dy/dx for each x, and
    the third dimension spans gradients dy/dx for each y.

    For example, passing an array with 100 samples, 8 inputs and 2 outputs will
    return an array of size (100, 8, 2) where (:, :, 0) contains all dy1/dx and
    (:, :, 1) contains all dy2/dx.

    The workflow of this method is as follows:
        1. Import xy data in array of size (m, n_x + n_y) and split into x, y
        2. Generate dy in n_y arrays of size (m-1, n_x) which correspond to
        points between samples
        3. Normalize x, dy on [0, 1] and train MLP model dy(x) for each dy
        4. Predict dy(x) for m samples from xy data to generate n_y arrays of
        size (m, n_x) which correspond to sample points
        5. Concatenate predicted gradients into array of size (m, n_x, n_y)
    """

    # split data into inputs and outputs
    x = xy_data[:, :n_x]  # there are n_x input variables/columns
    y = xy_data[:, n_x:]  # the rest are output variables/columns
    n_y = np.shape(y)[1]  # save number of outputs
    n_m = np.shape(y)[0]  # save number of samples

    gradients = []  # empty list to hold gradient arrays for multiple outputs

    for output in range(n_y):
        print("Generating gradients for output ", output, ":")
        # estimate first-order gradients using finite difference approximation
        # this will account for all input variables, but will be for the midpoints
        # between the sample points, i.e. len(y) - len(dy_midpoints) = 1.
        # in both midpoints and gradients_midpoints, each column corresponds to an
        # input variable xi and each row corresponds to a point between two samples
        midpoints = np.empty((n_m - 1, n_x))
        gradients_midpoints = np.empty((n_m - 1, n_x))

        # get midpoint gradients for one pair of samples at a time and save
        for m in range(n_m - 1):  # we have (n_m - 1) adjacent sample pairs
            print("Midpoint gradient ", m + 1, " of ", n_m - 1, " generated.")
            midpoints[m], gradients_midpoints[m] = finite_difference(
                m1=x[m, :],
                m2=x[m + 1, :],
                y1=y[m][output],  # each entry in y is an array somehow
                y2=y[m + 1][output],  # each entry in y is an array somehow
                n_x=n_x,
                use_simple_diff=use_simple_diff,
            )
        print("Midpoint gradient generation complete.")
        print()

        # leverage NN regression to predict gradients at sample points
        gradients.append(
            predict_gradients(
                midpoints=midpoints,
                gradients_midpoints=gradients_midpoints,
                x=x,
                n_m=n_m,
                n_x=n_x,
                show_plots=show_plots,
                optimize_training=optimize_training,
            )
        )

    return gradients

if __name__ == "__main__":
    data = pd.read_csv(r"MEA_carbon_capture_dataset_mimo.csv")
    data_array = np.array(data, ndmin=2)
    n_x = 6

    gradients = generate_gradients(
        xy_data=data_array,
        n_x=n_x,
        show_plots=False,
        optimize_training=True,
        use_simple_diff=True,
    )
    print("Gradient generation complete.")

    for output in range(len(gradients)):
        pd.DataFrame(gradients[output]).to_csv(
            "gradients_output" + str(output) + ".csv"
        )
        print(
            "Gradients for output ",
            str(output),
            " written to gradients_output" + str(output) + ".csv",
        )


# coding: utf-8

"""
    IONOS Cloud - DNS API

    Cloud DNS service helps IONOS Cloud customers to automate DNS Zone and Record management.   # noqa: E501

    The version of the OpenAPI document: 1.12.0
    Contact: support@cloud.ionos.com
    Generated by: https://openapi-generator.tech
"""

import pprint
import re  # noqa: F401

import six

from ionoscloud_dns.configuration import Configuration

class MetadataForSecondaryZoneRecords(object):
    """NOTE: This class is auto generated by OpenAPI Generator.
    Ref: https://openapi-generator.tech

    Do not edit the class manually.
    """

    """
    Attributes:
      openapi_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    openapi_types = {

        'fqdn': 'str',

        'zone_id': 'str',

        'root_name': 'str',
    }

    attribute_map = {

        'fqdn': 'fqdn',

        'zone_id': 'zoneId',

        'root_name': 'rootName',
    }

    def __init__(self, fqdn=None, zone_id=None, root_name=None, local_vars_configuration=None):  # noqa: E501
        """MetadataForSecondaryZoneRecords - a model defined in OpenAPI"""  # noqa: E501
        if local_vars_configuration is None:
            local_vars_configuration = Configuration()
        self.local_vars_configuration = local_vars_configuration

        self._fqdn = None
        self._zone_id = None
        self._root_name = None
        self.discriminator = None

        self.fqdn = fqdn
        if zone_id is not None:
            self.zone_id = zone_id
        self.root_name = root_name

    @property
    def fqdn(self):
        """Gets the fqdn of this MetadataForSecondaryZoneRecords.  # noqa: E501

        A fully qualified domain name. FQDN consists of two parts - the hostname and the domain name.  # noqa: E501

        :return: The fqdn of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :rtype: str
        """
        return self._fqdn

    @fqdn.setter
    def fqdn(self, fqdn):
        """Sets the fqdn of this MetadataForSecondaryZoneRecords.

        A fully qualified domain name. FQDN consists of two parts - the hostname and the domain name.  # noqa: E501

        :param fqdn: The fqdn of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :type fqdn: str
        """
        if self.local_vars_configuration.client_side_validation and fqdn is None:  # noqa: E501
            raise ValueError("Invalid value for `fqdn`, must not be `None`")  # noqa: E501

        self._fqdn = fqdn

    @property
    def zone_id(self):
        """Gets the zone_id of this MetadataForSecondaryZoneRecords.  # noqa: E501

        The ID (UUID) of the DNS zone of which record belongs to.  # noqa: E501

        :return: The zone_id of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :rtype: str
        """
        return self._zone_id

    @zone_id.setter
    def zone_id(self, zone_id):
        """Sets the zone_id of this MetadataForSecondaryZoneRecords.

        The ID (UUID) of the DNS zone of which record belongs to.  # noqa: E501

        :param zone_id: The zone_id of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :type zone_id: str
        """

        self._zone_id = zone_id

    @property
    def root_name(self):
        """Gets the root_name of this MetadataForSecondaryZoneRecords.  # noqa: E501

        Indicates the root name (from the primary zone) for the record  # noqa: E501

        :return: The root_name of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :rtype: str
        """
        return self._root_name

    @root_name.setter
    def root_name(self, root_name):
        """Sets the root_name of this MetadataForSecondaryZoneRecords.

        Indicates the root name (from the primary zone) for the record  # noqa: E501

        :param root_name: The root_name of this MetadataForSecondaryZoneRecords.  # noqa: E501
        :type root_name: str
        """
        if self.local_vars_configuration.client_side_validation and root_name is None:  # noqa: E501
            raise ValueError("Invalid value for `root_name`, must not be `None`")  # noqa: E501

        self._root_name = root_name
    def to_dict(self):
        """Returns the model properties as a dict"""
        result = {}

        for attr, _ in six.iteritems(self.openapi_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):
                result[attr] = dict(map(
                    lambda item: (item[0], item[1].to_dict())
                    if hasattr(item[1], "to_dict") else item,
                    value.items()
                ))
            else:
                result[attr] = value

        return result

    def to_str(self):
        """Returns the string representation of the model"""
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        if not isinstance(other, MetadataForSecondaryZoneRecords):
            return False

        return self.to_dict() == other.to_dict()

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        if not isinstance(other, MetadataForSecondaryZoneRecords):
            return True

        return self.to_dict() != other.to_dict()


import csv
import json

d = {}

def hello(stringToInt):
    try:
        return int(stringToInt)
    except ValueError:
        return None

with open("stats.csv") as csvfile:
    reader = csv.reader(csvfile)
    rows = []
    for row in reader:
        rows.append(row)

    for j in range(2, len(rows)):
        row = rows[j]
        item = row[1]
        for i in range(2, len(row), 3):
            district = rows[0][i].lower()

            area = hello(row[i].replace(",", "").strip())
            production = hello(row[i+1].replace(",", "").strip())
            yield_ = hello(row[i+2].replace(",", "").strip())

            d.setdefault(district, {})
            d[district].setdefault(item, {})
            d[district][item]["production"] = production
            d[district][item]["area"] = area
            d[district][item]["yield"] = yield_

    # combine nawalpur and parasi
    d.setdefault("nawalparasi", {})
    d["nawalparasi"].setdefault(item, {})
    for key, item in d["nawalpur"].items():
        d["nawalparasi"][key] = item
    for key, item in d["parasi"].items():
        for k2, i2 in item.items():
            d["nawalparasi"][key][k2] = d["nawalparasi"][key][k2] + \
                i2 if d["nawalparasi"][key][k2] else None
    d.pop("nawalpur")
    d.pop("parasi")

    # remove province/country info
    to_remove = [key for key in d.keys() if "province" in key or "nepal" in key]
    for key in to_remove:
        d.pop(key)

    with open("out.json", "w") as output_file:
        json.dump(d, output_file)


import numpy as np
from bathy_smoother import LP_bathy_tools
from bathy_smoother import LP_tools
from bathy_smoother import bathy_tools

import matplotlib.pyplot as plt

# This code is adapted from the matlab code
# "LP Bathymetry" by Mathieu Dutour Sikiric
# http://drobilica.irb.hr/~mathieu/Bathymetry/index.html
# For a description of the method, see
# M. Dutour Sikiric, I. Janekovic, M. Kuzmic, A new approach to
# bathymetry smoothing in sigma-coordinate ocean models, Ocean
# Modelling 29 (2009) 128--136.

def LP_smoothing_rx0(MSK, Hobs, rx0max, SignConst, AmpConst):
    """
    This program perform a linear programming method in order to
    optimize the bathymetry for a fixed factor r.
    The inequality |H(e)-H(e')| / (H(e)-H(e')) <= r where H(e)=h(e)+dh(e)
    can be rewritten as two linear inequalities on dh(e) and dh(e').
    The optimal bathymetry is obtain by minimising the perturbation
    P = sum_e(|dh(e)| under the above inequalitie constraintes.

    Usage:
    NewBathy = LP_smoothing_rx0(MSK, Hobs, rx0max, SignConst, AmpConst)

    ---MSK(eta_rho,xi_rho) is the mask of the grd
         1 for sea
         0 for land
    ---Hobs(eta_rho,xi_rho) is the raw depth of the grid
    ---rx0max is the target rx0 roughness factor
    ---SignConst(eta_rho,xi_rho) matrix of 0, +1, -1
         +1  only bathymetry increase are allowed.
         -1  only bathymetry decrease are allowed.
         0   increase and decrease are allowed.
         (put 0 if you are indifferent)
    ---AmpConst(eta_rho,xi_rho)  matrix of reals.
         coefficient alpha such that the new bathymetry should
         satisfy to  |h^{new} - h^{raw}| <= alpha h^{raw}
         (put 10000 if you are indifferent)
    """

    eta_rho, xi_rho = MSK.shape

    iList, jList, sList, Constant = LP_bathy_tools.GetIJS_rx0(MSK, Hobs, rx0max)

    iListApp, jListApp, sListApp, ConstantApp = LP_bathy_tools.GetIJS_maxamp(MSK, Hobs, AmpConst)

    iList, jList, sList, Constant = LP_bathy_tools.MergeIJS_listings(iList, jList, sList, Constant, iListApp, jListApp, sListApp, ConstantApp)

    iListApp, jListApp, sListApp, ConstantApp = LP_bathy_tools.GetIJS_signs(MSK, SignConst)

    iList, jList, sList, Constant = LP_bathy_tools.MergeIJS_listings(iList, jList, sList, Constant, iListApp, jListApp, sListApp, ConstantApp)

    TotalNbVert = int(MSK.sum())

    ObjectiveFct = np.zeros((2*TotalNbVert,1))
    for iVert in range(TotalNbVert):
        ObjectiveFct[TotalNbVert+iVert,0] = 1

    ValueFct, ValueVar, testfeasibility = LP_tools.SolveLinearProgram(iList, jList, sList, Constant, ObjectiveFct)
    if (testfeasibility == 0):
        NewBathy = NaN * np.ones((eta_rho,xi_rho))
        raise ValueError('Feasibility test failed. testfeasibility = 0.')

    correctionBathy = np.zeros((eta_rho,xi_rho))
    nbVert = 0
    for iEta in range(eta_rho):
        for iXi in range(xi_rho):
            if (MSK[iEta,iXi] == 1):
                correctionBathy[iEta,iXi] = ValueVar[nbVert]
                nbVert = nbVert + 1

    NewBathy = Hobs + correctionBathy
    RMat = bathy_tools.RoughnessMatrix(NewBathy, MSK)
    MaxRx0 = RMat.max()
    print('rx0max = ', rx0max, '  MaxRx0 = ', MaxRx0)

    return NewBathy

def LP_smoothing_rx0_heuristic(MSK, Hobs, rx0max, SignConst, AmpConst):
    """
    This program perform a linear programming method in order to
    optimize the bathymetry for a fixed factor r.
    The inequality |H(e)-H(e')| / (H(e)-H(e')) <= r where H(e)=h(e)+dh(e)
    can be rewritten as two linear inequalities on dh(e) and dh(e').
    The optimal bathymetry is obtain by minimising the perturbation
    P = sum_e(|dh(e)| under the above inequalitie constraintes.
    In order to reduce the computation time, an heurastic method is
    used.

    Usage:
    NewBathy = LP_smoothing_rx0_heuristic(MSK, Hobs, rx0max, SignConst, AmpConst)

    ---MSK(eta_rho,xi_rho) is the mask of the grd
         1 for sea
         0 for land
    ---Hobs(eta_rho,xi_rho) is the raw depth of the grid
    ---rx0max is the target rx0 roughness factor
    ---SignConst(eta_rho,xi_rho) matrix of 0, +1, -1
         +1  only bathymetry increase are allowed.
         -1  only bathymetry decrease are allowed.
         0   increase and decrease are allowed.
         (put 0 if you are indifferent)
    ---AmpConst(eta_rho,xi_rho)  matrix of reals.
         coefficient alpha such that the new bathymetry should
         satisfy to  |h^{new} - h^{raw}| <= alpha h^{raw}
         (put 10000 if you are indifferent)
    """

    # the points that need to be modified
    MSKbad = LP_bathy_tools.GetBadPoints(MSK, Hobs, rx0max)

    eta_rho, xi_rho = MSK.shape

    Kdist = 5

    Kbad = np.where(MSKbad == 1)
    nbKbad = np.size(Kbad,1)
    ListIdx = np.zeros((eta_rho,xi_rho), dtype=np.int)
    ListIdx[Kbad] = list(range(nbKbad))

    ListEdges = []
    nbEdge = 0
    for iK in range(nbKbad):
        iEta, iXi = Kbad[0][iK], Kbad[1][iK]
        ListNeigh = LP_bathy_tools.Neighborhood(MSK, iEta, iXi, 2*Kdist+1)
        nbNeigh = np.size(ListNeigh, 0)
        for iNeigh in range(nbNeigh):
            iEtaN, iXiN = ListNeigh[iNeigh]
            if (MSKbad[iEtaN,iXiN] == 1):
                idx = ListIdx[iEtaN,iXiN]
                if (idx > iK):
                    nbEdge = nbEdge + 1
                    ListEdges.append([iK, idx])

    ListEdges = np.array(ListEdges)
    ListVertexStatus = LP_bathy_tools.ConnectedComponent(ListEdges, nbKbad)
    nbColor = ListVertexStatus.max()

    NewBathy = Hobs.copy()
    for iColor in range(1,nbColor+1):
        print('---------------------------------------------------------------')
        MSKcolor = np.zeros((eta_rho, xi_rho))
        K = np.where(ListVertexStatus == iColor)
        nbK = np.size(K,1)
        print('iColor = ', iColor, '  nbK = ', nbK)
        for iVertex in range(nbKbad):
            if (ListVertexStatus[iVertex,0] == iColor):
                iEta, iXi = Kbad[0][iVertex], Kbad[1][iVertex]
                MSKcolor[iEta, iXi] = 1
                ListNeigh = LP_bathy_tools.Neighborhood(MSK, iEta, iXi, Kdist)
                nbNeigh = np.size(ListNeigh, 0)
                for iNeigh in range(nbNeigh):
                    iEtaN, iXiN = ListNeigh[iNeigh]
                    MSKcolor[iEtaN,iXiN] = 1
        K = np.where(MSKcolor == 1)
        MSKHobs = np.zeros((eta_rho, xi_rho))
        MSKHobs[K] = Hobs[K].copy()
        TheNewBathy = LP_smoothing_rx0(MSKcolor, MSKHobs, rx0max, SignConst, AmpConst)
        NewBathy[K] = TheNewBathy[K].copy()

    print('Final obtained bathymetry')
    RMat = bathy_tools.RoughnessMatrix(NewBathy, MSK)
    MaxRx0 = RMat.max()
    print('rx0max = ', rx0max, '  MaxRx0 = ', MaxRx0)

    return NewBathy


from graphics import *

def main():
    win = GraphWin("Gráfico Função", 500, 500)
    win.setBackground('white')
    img = Image(Point(250, 250), "graf.gif")
    img.draw(win)
    x = -6
    while True:
        y = x
        if x > 6:
            break
        if x < 0:
            xpos = 250 - (250*(0-x)) / 6
            if y < 0:
                ypos = 250 + (250 * (0 - y)) / 6
            else:
                ypos = 250 - (250 * y) / 6
            p2 = Point(xpos, ypos)
            if x == -6:
                p1 = Point(xpos, ypos)
        elif x >= 0:
            xpos = 250 + (250 * x) / 6
            if y < 0:
                ypos = 250 + (250 * (0 - y)) / 6
            else:
                ypos = 250 - (250 * y) / 6
            p2 = Point(xpos, ypos)
        ln = Line(p1, p2)
        ln.setOutline('blue')
        ln.setWidth(3)
        ln.draw(win)
        x += 0.01
        p1 = p2
    win.getMouse()
    win.close()

main()


from itertools import product
from typing import Union, List

class Solution:
    def expandA(self, s: str) -> List[str]:
        """
        You are given a string s representing a list of words. Each letter in
        the word has one or more options.

        If there is one option, the letter is represented as is.
        If there is more than one option, then curly braces delimit the
        options. For example, "{a,b,c}" represents options ["a", "b", "c"].
        For example, if s = "a{b,c}", the first character is always 'a', but
        the second character can be 'b' or 'c'. The original list is ["ab",
        "ac"].

        Return all words that can be formed in this manner, sorted in
        lexicographical order.
        """
        counting = False
        opt = []
        res: list[Union[list, str]] = []
        for c in s:
            if c == "{":
                counting = True
            elif c == "}":
                counting = False
                res.append(opt)
                opt = []
            elif c == ",":
                continue
            elif counting:
                opt.append(c)
            else:
                res.append(c)
        return sorted(["".join(p) for p in product(*res)])


'''
Ian L.
George M.

Task 1.)
this python file will contain the library for our HTTP client.
This library will support these features:
a.) GET operation - Retreives data from the server
b.) POST operation - Submits data to the server
c.) Query parameters
d.) Request headers
e.) Body of the request

Task 2.)
Implementation of a cURL command line that does basic functionalities that
are related to the HTTP

'''
import socket	#for sockets
import sys	#for exit
import time

import argparse
from typing import Text
from xmlrpc.client import Boolean
from urllib.parse import urlparse

def get_request(url, port, verbose=False, headers = None):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    urlparser = urlparse(url)
    #host is 'httpbin.org', a "part" of the url variable/ full url that was passed in
    host = urlparser.hostname
    try:
        #host: 'www.httpbin.org'
        #url: http://httpbin.org/get?course=networking&assignment=1
        client.connect((host, port))

        request = "GET " + url + " HTTP/1.0\r\n" \
        "Host:%s\r\n" % host
        #check if we have passed in a list of headers from our -H arg
        if headers != None:
            #each key:value must end with \r\n (It's essentially the delimeter for lines
            # in a request, look at slides for chapter 2), but that is what is going on below
            for header in headers:
                request += header + "\r\n"

        #every request that is sent to the server must end with one last additional \r\n,
        #that is what is happening below
        request+= "\r\n"
        request = request.encode("utf-8")

        client.sendall(request)
        # MSG_WAITALL waits for full request or error
        #og is response = client.recv(1024)
        response = recv_timeout(client)
        response_details, response_data = response.split("\r\n\r\n")
        #show details with verbose if activated
        if(verbose):
            print(response_details, "\n")
        print(response_data)
    except socket.error:
        #Send failed
        print('Send failed')
        sys.exit()
    except ValueError:
        print(response)
    finally:
        client.close()

def post_request(url, port, verbose=False, headers = None, inline_data= None, file = None):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    urlparser = urlparse(url)
    #host is 'httpbin.org', a "part" of the url variable/ full url that was passed in
    host = urlparser.hostname
    data = None
    try:
        #host: 'www.httpbin.org'
        #url: http://httpbin.org/get?course=networking&assignment=1
        client.connect((host, port))

        request = "POST " + url + " HTTP/1.0\r\n" \
            "Host:%s\r\n" % host

        #supply content-length
        #by default using post request you must supply a content length header
        request+="Content-Length: 17\r\n"
        #check if we have passed in a list of headers from our -H arg
        if headers != None:
            #each key:value must end with \r\n (It's essentially the delimeter for lines
            # in a request, look at slides for chapter 2), but that is what is going on below
            for header in headers:
                request += header + "\r\n"
        key, value = None, None
        # check the combinations of the -d and -f cases
        if (inline_data and not file):
            if(": " in inline_data):
                key, value = inline_data.split(": ")
            else:
                data = inline_data
        elif (not inline_data and file):
            file = open("sample.txt")
            line = file.read()
            try:
                key, value = line.split(": ")
            except ValueError:
                print("Not correct key-value pair format. Try again.")
        else:
            print ("Must have either one of the two valid arguments: -d or -f")
        #must also provided double quotes around the value field like in assignment pdf
        if(data != inline_data):
            data = '''{"%s": %s}",'''%(key,value)
        request += "\r\n"
        request+=data
        request+= "\r\n"
        #print(request)
        request = request.encode("utf-8")
        client.sendall(request)

        response = client.recv(1024)
        full_response = response.decode("utf-8")
        #split() returns a string list that is seperated by what you sent as an arg.
        response_details, response_data = full_response.split("\r\n\r\n")
        #show details with verbose if activated
        if(verbose):
            print(response_details, "\n")
        print(response_data)
    except ValueError:
        print(full_response)
    finally:
        client.close()

def recv_timeout(the_socket,timeout=2):
    #make socket non blocking
    the_socket.setblocking(0)

    #total data partwise in an array
    total_data=[]
    data=''

    #beginning time
    begin=time.time()
    while 1:
        #if you got some data, then break after timeout
        if total_data and time.time()-begin > timeout:
            break

        #if you got no data at all, wait a little longer, twice the timeout
        elif time.time()-begin > timeout*2:
            break

        #recv something
        try:
            data = the_socket.recv(8192)
            if data:
                response = data.decode("utf-8")
                total_data.append(response)
                #change the beginning time for measurement
                begin = time.time()
            else:
                #sleep for sometime to indicate a gap
                time.sleep(0.1)
        except:
            pass

    #join all parts to make final string
    return ''.join(total_data)

#method for help text
def print_help_for_post_or_get(argument):
    if(argument == 'get' or argument == 'GET'):
        print('httpc --HELP get'\
                '\nusage: httpc --get "URL" [-v] [-h key:value]\n' \
                'Get executes a HTTP GET request for a given URL.\n' \
                '-v\t\tPrints the detail of the response such as protocol, status, and headers.\n'\
                "-h key:value\tAssociates headers to HTTP Request with the format 'key:value'.")
    elif(argument =='post' or argument=='POST'):
        print('httpc --HELP post'\
                '\nusage: httpc --post "URL" [-v] [-h key:value] [-d inline-data] [-f file]\n' \
                'Post executes a HTTP POST request for a given URL with inline data or from file.\n' \
                '-v\t\tPrints the detail of the response such as protocol, status, and headers.\n'\
                '-h key:value\t\tAssociates headers to HTTP Request with the format\n'\
                '-d string\t\tAssociates headers to HTTP Request with the format\n'\
                '-f file\t\tAssociates the content of a file to the body HTTP POST request.\n'\
                "Either [-d] or [-f] can be used but not both.")

def main():
    #parser is container to hold our arguments
    #for better understanding of the argparser
    #visit: https://realpython.com/command-line-interfaces-python-argparse/
    parser = argparse.ArgumentParser(prog='httpc',
                                    description='httpc is a curl-like application but supports HTTP protocol only.',
                                    usage="\n\thttpclient.py command [arguments]", allow_abbrev=False,
                                    epilog='Use "httpclient.py help [command]" for more information about a command.')
    '''
    Syntactically, the difference between positional and optional arguments is
    that optional arguments start with - or --, while positional arguments do not.
    We will be using optional for the most part
    '''
    #optional arguments
    parser.add_argument("--host", help="Input the server host ip", default="localhost", action="store")
    parser.add_argument("--port", help="Input the server port number", type=int, default=80, action="store")
    parser.add_argument("--get", help="executes a HTTP GET request and prints the response. For URLs, you must surround it around double quotes.")
    parser.add_argument("--post", help="executes a HTTP POST request and prints the response.")

    #store_true sends a true value when sent once!
    parser.add_argument("--HELP",help="Display the help text for the --get and --post request as well as other options for these requests."
                         , type=str, choices=['GET', 'get', 'POST', 'post'], required=False)
    #more optional arguments that can go with the above, -H, _v for example
    #this one below is -H for the headers "key:value" requirement
    '''
    3. To pass the headers value to your HTTP operation, you could use -H option. The latter means
    setting the header of the request in the format "key: value." Notice that; you can have
    multiple headers by having the -H option before each header parameter.
    WE WILL USE -H TO NOT CONFLICT WITH -h the default help feature argparse uses!
    -h is the default argparse help option that cannot be changed.
    '''
    parser.add_argument('-H', required=False, help='Associates headers to HTTP Request with the format "key:value". You must pass the headers one by one starting with -H followed up by a space then the key:value which should be surrounded by double quotes.' ,action="append")
    parser.add_argument("-v", "--verbose", help="Turns on verbose mode for more details",
                      action="store_true",required=False)
    parser.add_argument('-d', required=False, help="Associates an inline data to the body HTTP POST request.")
    parser.add_argument('-f', required=False, help="Associate the body of the HTTP Request with the data from a given file.")
    #required argument in the params above "required=" can be used to force a user
    #to add an argument, could be useful
    '''
    After you execute .parse_args(), what you get is a Namespace object that contains a simple
    property for each input argument received from the command line.
    //////////////////////////////////////////////
    Example usages in cmd prompt:
    #header option examples: (THEY MUST BE SURROUNDED BY QUOTES)
        "Accept-Language: en us,en;q=0.5"
        "Accept-Encoding: gzip,deflate"
        "Content-Type:application/json"

    httpclient.py --get "http://httpbin.org/headers" -v -H "Accept-Language: en us,en;q=0.5" -H "Content-Type: application/json; charset=utf-8"
    httpclient.py --post "http://httpbin.org/post" -v -d "Assignment: 1" -H "Content-Type: application/json"
    httpclient.py --post "http://httpbin.org/post" -v -f sample.txt -H "Content-Type: application/json"
    httpclient.py --post "http://httpbin.org/post" -v
    httpclient.py --HELP get

    local server communication:

    GET /
    httpclient.py --port 1234 --get "http://localhost/"
    httpclient.py --port 1234 --get "http://localhost/Directory3/"
    httpclient.py --port 1234 --get "http://localhost/Directory2/"

    GET /filename.type
    httpclient.py --port 1234 --get "http://localhost/bar.txt"
    httpclient.py --port 1234 --get "http://localhost/Directory3/bar.txt"
    httpclient.py --port 1234 --get "http://localhost/Directory3/foo_2.txt"
    httpclient.py --port 1234 --get "http://localhost/foo3.txt"
    httpclient.py --port 1234 --get "http://localhost/empty.txt"
    httpclient.py --port 1234 --get "http://localhost/bar"

    httpclient.py --port 1234 --post "http://localhost/bar" -d "Test text"
    httpclient.py --port 1234 --post "http://localhost/Directory3/bar.html" -d "<h>Hello World!</h>"
    if we want to use 'key: value' pairing:
        httpclient.py --port 1234 --post "http://localhost/bar.txt" -d "Assignment: 1"
    //////////////////////////////////////////////
    '''

    args = parser.parse_args()
    if(args.get != None):
        get_request(args.get, args.port, args.verbose, args.H)
    elif(args.post != None):
        post_request(args.post, args.port, args.verbose, args.H, args.d, args.f)
    elif(args.HELP != None):
        print_help_for_post_or_get(args.HELP)

if(__name__=="__main__"):
    main()


from __future__ import division
import math
from math import atan2, degrees, pi
import logging

class Vector:
    # Represents a vector between two Positions.
    def __init__(self, x=0.0, y=0.0, pos_from=None, pos_to=None):
        if pos_from is None:
            self.x = x
            self.y = y
        else:
            self.x = pos_to.x - pos_from.x
            self.y = pos_to.y - pos_from.y

    # The length of the vector.
    @property
    def length(self):
        x_squared = self.x * self.x
        y_squared = self.y * self.y
        return round(math.sqrt(x_squared + y_squared), 2)

    @property
    def direction(self):
        rads = atan2(self.x, -self.y)
        rads %= 2*pi
        degs = degrees(rads)
        return round(degs, 2)

    # Returns a new Vector, scaled to the length passed in.
    def get_scaled_vector(self, length):
        if self.length == 0.0:
            return Vector(0.0, 0.0)
        else:
            x = self.x * length / self.length
            y = self.y * length / self.length
            return Vector(x, y)

    def get_rotated_vector(self, deg):
        cos = math.cos(math.radians(deg))
        sin = math.sin(math.radians(deg))

        new_x = self.x*cos - self.y*sin
        new_y = self.y*cos + self.x*sin

        return Vector(x=new_x, y=new_y)

    def get_opposite_vector(self):
        return Vector(x=-self.x, y=-self.y)

    def angle_from(self, vector=None, direction=-1):
        # Players turn at 600 degrees per second
        if isinstance(vector, Vector):
            deg_diff = abs(vector.direction - self.direction)
        elif direction >= 0:
            deg_diff = abs(direction - self.direction)
        else:
            raise ValueError("Didn't input vector or direction in angle_from method")

        if deg_diff > 180:
            deg_diff = 360 - deg_diff
            # logging.debug("the deg_diff was bigger than 180, changing to [%d]"%(deg_diff))

        return deg_diff

    def turning_time(self, vector=None, direction=-1):
        # Players turn at 600 degrees per second
        return math.ceil(self.angle_from(vector=vector, direction=direction) / 600 * 100000) / 100000


#/usr/rin/python3
"""

Tests for Base

"""
import unittest
from models.rectangle import Rectangle
from models.base import Base
import json

class BaseTest(unittest.TestCase):

    def test_id_normal(self):
        b1 = Base()
        self.assertEqual(b1.id, 1)

    def test_id_one_args(self):
        b2 = Base(2)
        self.assertEqual(b2.id, 2)

    def test_id_two_args(self):
        with self.assertRaises(TypeError):
            b3 = Base(89, 2)

    def test_nb(self):
        b = Base(3)
        with self.assertRaises(AttributeError):
            str(b.nb_objects)
        with self.assertRaises(AttributeError):
            str(b.__nb_objects)

    def test_to_json_string(self):
        d1 = {"id": 1, "width": 5, "height": 12, "x": 1, "y": 3}
        d2 = {"id": 3, "width": 6, "height": 3, "x": 8, "y": 2}
        j = Base.to_json_string([d1, d2])
        self.assertFalse(isinstance(j, list))
        pobj = json.loads(j)
        self.assertEqual(pobj, [d1, d2])
        self.assertTrue(isinstance(pobj, list))

    def test_from_json_string(self):
        j = '[{"id": 1, "width": 5, "height": 12, "x": 1, "y": 3}, \
            {"id": 3, "width": 6, "height": 3, "x": 8, "y": 2}]'
        dic = Base.from_json_string(j)
        self.assertTrue(isinstance(dic, list))

    def test_none_to_json_string(self):
        """Passing none to empty list"""
        json_s = Base.to_json_string(None)
        self.assertEqual(json_s, "[]")

    def test_empty_to_json_string(self):
        """Passing empty list"""
        json_s = Base.to_json_string([])
        self.assertTrue(isinstance(json_s, str))
        self.assertEqual(json_s, "[]")
#    def tearDown(self):
 #       self.r1.dispose()
  #      self.r2.dispose()
   #     self.r3.dispose()
    #    self.r4.dispose()


def calculate(self, T, method):
        r'''Method to calculate the molar volume of a solid at tempearture `T`
        with a given method.

        This method has no exception handling; see `T_dependent_property`
        for that.

        Parameters
        ----------
        T : float
            Temperature at which to calculate molar volume, [K]
        method : str
            Name of the method to use

        Returns
        -------
        Vms : float
            Molar volume of the solid at T, [m^3/mol]
        '''
        if method == CRC_INORG_S:
            Vms = self.CRC_INORG_S_Vm
#        elif method == GOODMAN:
#            Vms = Goodman(T, self.Tt, self.rhol_Tt)
        elif method in self.tabular_data:
            Vms = self.interpolate(T, method)
        return Vms


"""import sys
sys.stdin = open("input.txt", "rt")
"""
N, K = map(int, input().split())
#N, K = 20, 3
arr = list(range(1, N+1))
cnt = 0
"""1 2 4 5 6 7 8 [다음꺼]
1 2 5 6 7 8 [다음꺼]
#1. remove + index로 제거하는 방법이 있지만 사용 안할꺼
#2. queue 로 쌓아보자.

cnt = 0
arr = [1, 2, 3, 4, 5, 6, 7, 8]
"""

while len(arr) > 1:
    for i in arr:
        cnt += 1
        if cnt == K:
            arr.remove(i)
            arr = arr[K - 1:] + arr[ : K - 1]
            cnt = 0
            break

print(arr[0])


import cv2 as cv

img = cv.imread('Photos/cats.jpg')
cv.imshow('img',img)

"""Averaging"""
# average = cv.blur(img,(7,7)) # src,ksize,dst=None,anchor=None borderType=None
# cv.imshow('average',average)

"""GaussinaBlur"""
# gauss = cv.GaussianBlur(img,(3,3),0)
# cv.imshow('gauss',gauss)

"""medianBlur"""

# median = cv.medianBlur(img,3)
# cv.imshow('median',median)

"""Bilateral"""
# bilateral = cv.bilateralFilter(img,5,15,15) #src,diameter,sigmaColor,sigmaSpace
# cv.imshow('bilateral',bilateral)

cv.waitKey(0)


# *****************************************************************************
# ПРОЕКТ: ISS Docking Simulator
# ФАЙЛ: game.py
# ОПИСАНИЕ: основной модуль проекта
# *****************************************************************************

# -- ИМПОРТ БИБЛИОТЕК
import pygame, os, datetime, math as Math
from pygame.locals import *
from padlib import *

os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (50,50) # зададим координаты вывода главного окна

# ---- ФУНКЦИИ
def refineDest(sh, sv):
	a = set()
	if round(sh, 1) > 0.0:
		a.add("ВОСТОК")
	if round(sh, 1) < 0.0:
		a.add("ЗАПАД")
	if round(sv, 1) > 0.0:
		a.add("ЮГ")
	if round(sv, 1) < 0.0:
		a.add("СЕВЕР")
	return a

# -- ИНИЦИАЛИЗАЦИЯ
random.seed()
pygame.init()
screen = pygame.display.set_mode((800, 600)) # главная поверхность
pygame.display.set_caption('IIS Docking simulator')

pygame.font.init()
font_dot = pygame.font.Font('square_dot_digital-7.ttf', 30)

# -- ЗАДАЕМ НАЧАЛЬНЫЕ НАСТРОЙКИ
# загрузка картинок
img_scr_intro = pygame.image.load("Scr_Intro.jpg")
img_scr_task = pygame.image.load("Scr_Task.jpg")
img_progressm = pygame.image.load("img_progressm.png").convert_alpha()
img_background = pygame.image.load("background.jpg").convert()
img_brokenglass = pygame.image.load("img_brokenglass.png").convert_alpha()

# получить системный таймер для того, что бы задать FPS=25 кадров в секунду
clock = pygame.time.Clock()
fps = 25

# инициализация переменных
mainExit = False # флаг выхода из главного цикла
game_state = 0 # текущее состояние игры. То что будет обрабатывать главный цикл зависит от этой переменной
"""
0 - начальный экран №1 (введение)
1 - начальный экран №2 (задача)
2 - игровой экран (сближение со станцией)
3 - финальный экран (сигнал потерян / стыковка)

"""

dp_x, dp_y = random.randrange(100, 700), random.randrange(100, 500) # координаты Прогресса
dp_w, dp_h = 3119, 1276 # ширина и высота картинки Прогресса
dp_scale, dp_scale_step, dp_scale_stop = 10.0, 0.008, 0.5 # коэффициент уменьшения Прогресса. Используется для маштабирования картинки с целью симуляции приближения
dp_rotation, dp_rotation_step = random.randrange(0, 359), 0.2 # вращение Прогресса (0.2)
dp_scale_suspended = 0

# скорость корабля является линейной. По хорошему должна быть логарифмическая, но она для земного сопротивления по воздуху
speed_h, speed_v = 0.0, 0.0 # горизонтальная и вертикальная скорости
speed_max, speed_step = 9.0, 0.1 # максимальная скорость сближения, шаг увеличения скорости

# вычисляем движение в зависимости от появления
if dp_x < 400 and dp_y < 300: speed_h, speed_v = 1.0, 1.0
if dp_x < 400 and dp_y >= 300: speed_h, speed_v = 1.0, -1.0
if dp_x >= 400 and dp_y < 300: speed_h, speed_v = -1.0, 1.0
if dp_x >= 400 and dp_y >= 300: speed_h, speed_v = -1.0, -1.0

speed_direction = refineDest(speed_h, speed_v) # направление движения

iis_hit = False # True если Прогресс врезался в МКС
disp_interlaced = False

# константы
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
yellow = (255,255,0)

# расстояние между двумя точками по их координатам: от центра картинки до изображенного центра шлюза
docking_center = Math.sqrt(Math.pow((dp_w/2)-1604, 2) + Math.pow((dp_h/2)-705, 2))

# -- ОСНОВНОЙ ЦИКЛ ВЫПОЛНЕНИЯ ПРОГРАММЫ
while not mainExit:

	# обработка событий
	for event in pygame.event.get():
		# нажат крестик окна - выход из программы
		if event.type == QUIT:
			mainExit = True
		# если щелчок мыши или любой клавишей
		elif event.type == pygame.MOUSEBUTTONUP or event.type == pygame.KEYUP:
			if game_state < 2:
				game_state += 1
			elif game_state == 2:
				if event.key and event.key == K_SPACE:
					disp_interlaced = not disp_interlaced
				elif event.key and event.key == K_RETURN:
					if dp_scale_step != 0:
						dp_scale_suspended = dp_scale_step
						dp_scale_step = 0
					else:
						dp_scale_step = dp_scale_suspended

	# ОБРАБОТКА ВЫВОДА ВСЕХ ИЗОБРАЖЕНИЙ В ЭКРАННЫЙ БУФЕР
	if game_state == 0:
		""" основной экран """
		screen.blit(img_scr_intro, (0,0))

	elif game_state == 1:
		""" задача симуляции полета """
		screen.blit(img_scr_task, (0,0))

	elif game_state >= 2:
		""" симулятор """

	# если это игровой экран, задаем направление движения
		keys = pygame.key.get_pressed()
		if len(keys) != 0:
			if keys[K_LEFT]:
				if abs(speed_h) <= speed_max and dp_x < 800:
					speed_h += speed_step

			if keys[K_RIGHT]:
				if abs(speed_h) <= speed_max and dp_x > 0:
					speed_h -= speed_step

			if keys[K_UP]:
				if abs(speed_v) <= speed_max and dp_y < 600:
					speed_v += speed_step

			if keys[K_DOWN]:
				if abs(speed_v) <= speed_max and dp_y > 0:
					speed_v -= speed_step

			# ограничение на скорость
			if abs(speed_h) > speed_max:
				speed_h = speed_max * (-1 if speed_h < 0 else 1)
			if abs(speed_v) > speed_max:
				speed_v = speed_max * (-1 if speed_v < 0 else 1)

		# перемещение и вращение
		if dp_scale != dp_scale_stop:
			dp_x += speed_h
			dp_y += speed_v
			dp_rotation -= dp_rotation_step # вращение
			dp_rotation %= 360

		# ограничение на выход за пределы экрана
		if dp_x <= 0:
			dp_x = 0.0
			speed_h = 0
		if dp_y <= 0:
			dp_y = 0.0
			speed_v = 0
		if dp_x >= 800:
			dp_x = 800
			speed_h = 0.0
		if dp_y >= 600:
			dp_y = 600
			speed_v = 0.0

		speed_direction = refineDest(speed_h, speed_v)

		# вычисляем сближение
		dp_scale -= dp_scale_step
		if dp_scale < 1:
			dp_scale -= (dp_scale_step * 1.3)
		elif dp_scale < 2:
			dp_scale -= (dp_scale_step * 1.1)
		if dp_scale <= dp_scale_stop:
			# сближение закончено
			dp_scale = dp_scale_stop

		# очистили экран
		screen.fill(black)

		# поворачиваем Прогресс
		dp_rotated = pygame.transform.rotate(
				pygame.transform.scale(img_progressm, (int(dp_w/dp_scale), int(dp_h/dp_scale))),
				dp_rotation)
		#dp_rotated = pygame.transform.rotozoom(img_progressm, dp_rotation, 1/dp_scale)

		# высчитываем координаты
		r = docking_center / dp_scale

		# -- координаты вывода картинки
		xImg = dp_x - dp_rotated.get_rect().center[0]
		yImg = dp_y - dp_rotated.get_rect().center[1]

		# -- центр стыковки
		x0 = dp_x + r * Math.cos(Math.radians(57-dp_rotation))
		y0 = dp_y + r * Math.sin(Math.radians(57-dp_rotation))
		rc = pygame.Rect( int(x0-50/dp_scale), int(y0-50/dp_scale), int(100/dp_scale), int(100/dp_scale) )
		rc1 = pygame.Rect( int(x0-25/dp_scale), int(y0-25/dp_scale), int(50/dp_scale), int(50/dp_scale) )

		# отрисовываем Прогресс
		screen.blit(img_background, (0,0), (0,0,800,600))
		screen.blit(dp_rotated, (int(xImg), int(yImg)))

		# имитация чресстрочной развертки
		if disp_interlaced:
			for iy in range(0, 600, 2):
				pygame.draw.line(screen, (10,10,10), (0, iy), (800, iy), 1)

		# вычисляем находится ли перекрестие в допустимой зоне стыковки
		# а заодно определяем - успешно ли мы пристыковались или врезались
		# и выводим прямоугольник зоны стыковки
		if dp_scale < 5:
			if rc.collidepoint(400, 300):
				pygame.draw.rect(screen, green, rc, 2)
			else:
				pygame.draw.rect(screen, red, rc, 2)
		# вторая мишень
		if dp_scale < 2:
			if rc1.collidepoint(400, 300):
				pygame.draw.rect(screen, yellow, rc1, 1)
				if dp_scale == dp_scale_stop:
					iis_hit = False # успешно
					game_state = 3 # следующий игровой экран
			else:
				pygame.draw.rect(screen, red, rc1, 1)
				if dp_scale == dp_scale_stop:
					iis_hit = True # врезались
					game_state = 3 # следующий игровой экран

		# рисуем вид из камеры грузового корабля
		DashedLine(screen, white, black, (400, -13), (400, 599), 11) #верт линия
		DashedLine(screen, white, black, (-1, 300), (799, 300), 11) #гориз линия

		# выводим всякие типа служебные надписи для красоты
		fnt = font_dot.render("Ф44   СБЛИЖ         ПРИЧАЛ Т={}".format(
			       datetime.datetime.now().strftime("%H:%M:%S")), True, white)
		screen.blit(fnt, (95, 30))
		fnt = font_dot.render("ЛСК               ГСО 1234", True, white)
		screen.blit(fnt, (95, 60))
		fnt = font_dot.render("СКОРОСТЬ: {}H {}V".format(round(speed_h, 2), round(speed_v, 2)), True, white)
		screen.blit(fnt, (30, 110))
		fnt = font_dot.render("КУРС: {}".format( ("-".join(d for d in speed_direction)) if len(speed_direction) != 0 else "НЕТ"), True, white)
		screen.blit(fnt, (30, 140))
		fnt = font_dot.render("СБЛИЖЕНИЕ: {}".format(-1 * round(dp_scale - 1, 3)), True, white)
		screen.blit(fnt, (30, 190))
		fnt = font_dot.render("ВРАЩЕНИЕ: {}".format(round(dp_rotation % 360, 2)), True, white)
		screen.blit(fnt, (30, 220))
		fnt = font_dot.render("dX: {}".format(400-int(x0)), True, white)
		screen.blit(fnt, (650, 270))
		fnt = font_dot.render("dY: {}".format(300-int(y0)), True, white)
		screen.blit(fnt, (650, 300))
		fnt = font_dot.render("ДЕЛЬТА R {:f}".format(r), True, black)
		screen.blit(fnt, (30, 560))

		if dp_scale_step == 0:
			fnt = font_dot.render("SPEED PAUSED", True, white)
			screen.blit(fnt, (30, 330))
		if disp_interlaced:
			fnt = font_dot.render("INTERLACED VIEW", True, white)
			screen.blit(fnt, (30, 360))

		fnt = font_dot.render("ИН: ЕСТЬ ССВП ГТ", True, black)
		screen.blit(fnt, (420, 560))

		# если дистанция равна 0
		if dp_scale == dp_scale_stop:
			if iis_hit:
				screen.blit( pygame.transform.scale(img_brokenglass, (800, 600)), (0,0) )
				pygame.draw.rect(screen, red, (238,280,275, 40), 2)
				pygame.draw.rect(screen, red, (243,285,265, 30))
				fnt = font_dot.render("СИГНАЛ ПОТЕРЯН", True, white)
				screen.blit(fnt, (250,285))

			else:
				pygame.draw.rect(screen, green, (218,280,330, 40), 2)
				pygame.draw.rect(screen, green, (223,285,320, 30))
				fnt = font_dot.render("УСПЕШНАЯ СТЫКОВКА", True, black)
				screen.blit(fnt, (230,285))

	# обновить главную поверхность с частотой FPS
	pygame.display.flip()
	clock.tick(fps)

pygame.quit()


li=[(1,2),(3,4,5,6,7)]
a,*b=li
print(b)

'''迭代解包，先从第一个可迭代对象进行解包'''
for a,*b in li:
      print(b,end="..")

print("----------")
def out():
      out=9
      print("外部",out)
      def inner():
            print("inner")
            nonlocal out
            print("内部修改前",out)
            out+=1
            print("内部修改后",out)

            return  'over'

      return inner()
out()

# var 解析object含有dict属性，可以解析自定义的类实例
class person:

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

p=person('田七',23)
print('{name} has {age} age'.format_map(vars(p)))
print(vars(p))

#试着注册一个func，利用eval 解析字符串。当传入的字符串包含  参数名(x)  会将他解析
def func(a):
      #print(a+1)
      return a+1

eval("func(3)")

from case.lianxi.b import funb

#eval函数返回一个对象，打印对象，调用的是字符串内的变量的return结果
ev=eval("funb(3)")
print(ev)

def ex(c):
      print(c)
      return c

e=exec('ex(5)')         #这里只做执行函数处理 -执行函数内的print语句。
print(e)  #它的返回值是None

''' 使用exec 能够访问当前命名空间内的所有内容 locals globals'''

with open('H://test.txt', 'r') as f:
    ss = f.read()
    #print(ss)

exec(ss)

#exec('h://log.txt')

#使用列表推导式打印九九乘法表
#分析从左到右进行 先用  \n 换行符      使用格式化数字 %2d
#       两个for循环 第一个代表每行的数据，第二个代表总次数。执行时先执行第二个，第一个在最内层
print('\n'.join([' '.join(['%2d *%2d = %2d' % (col, row, col * row)
                           for col in range(1, row + 1)]) for row in range(1, 10)]))

jiujiu=( '\n'.join([ ''.join(['%2d *%2d =%2d'%(col,row,col*row)for col in range(1,row+1)])for row in range(1,10)]))

'''
自定义类，继承字典，添加魔法函数 missing
'''
class m(dict):

      def __missing__(self, key):
            return  '{'+'key'+'}'

m={'key1':1}
print('{key1}'.format_map(m(vars())))


from botpy import logging
from botpy.message import DirectMessage

from handler.handle_email_verification import handle_email_verification_direct_message
from utils.send_message_with_log import post_dms_with_log

_log = logging.get_logger()

async def direct_message_create_handler(client, message: DirectMessage):
    _log.info(
        f"收到私信消。Author ID: {message.author.id}, Author Username: {message.author.username}, Content: {message.content}, Timestamp: {message.timestamp}")
    content = message.content
    COMMANDS = {
        "/邮箱认证": handle_email_verification_direct_message
    }

    if content.startswith("/"):
        command = content.split()[0]  # 假设命令是消息中的第一个词
        handler = COMMANDS.get(command)

        if handler:
            await handler(client, message)
        else:
            await post_dms_with_log(client, message, content="无法识别的命令，请检查您的输入。")
    else:
        await post_dms_with_log(client, message, content="消息格式错误，请使用正确的命令格式。")