import os
import numpy as np
import xarray as xr
import dask.array as da
from glob import glob
import time
from datetime import datetime, timedelta
from satpy.scene import Scene
import satpy.utils
from satpy.readers.utils import remove_earthsun_distance_correction
from trollsift.parser import globify, Parser
from pyorbital.astronomy import get_alt_az, sun_zenith_angle
from pyorbital.orbital import get_observer_look

from level1c4pps.calibration_coefs import get_calibration, CalibrationData
from level1c4pps import make_azidiff_angle, get_encoding, compose_filename, update_angle_attributes

try:
    FileNotFoundError
except NameError:
    # Python 2
    FileNotFoundError = IOError

class UnexpectedSatpyVersion(Exception):
    """Exception if unexpected satpy version."""

    pass

BANDNAMES = ['VIS006', 'VIS008', 'IR_016', 'IR_039',
             'IR_087', 'IR_108', 'IR_120',
             'IR_134', 'IR_097', 'WV_062', 'WV_073']
PPS_TAGNAMES = {'VIS006': 'ch_r06',
                'VIS008': 'ch_r09',
                'IR_016': 'ch_r16',
                'IR_039': 'ch_tb37',
                'IR_087': 'ch_tb85',
                'IR_108': 'ch_tb11',
                'IR_120': 'ch_tb12',
                'IR_134': 'ch_tb133',
                'IR_097': 'ch_tb97',
                'WV_062': 'ch_tb67',
                'WV_073': 'ch_tb73'}

HRIT_FILE_PATTERN = ('{rate:1s}-000-{hrit_format:_<6s}-'
                     '{platform_shortname:_<12s}-{channel:_<8s}_-'
                     '{segment:_<9s}-{start_time:%Y%m%d%H%M}-__')
# H-000-MSG3__-MSG3________-IR_120___-000003___-201410051115-__

NATIVE_FILE_PATTERN = ('{platform_shortname:4s}-{instr:4s}-'
                       'MSG{product_level:2d}-{base_algorithm_version:4s}-NA-'
                       '{end_time:%Y%m%d%H%M%S.%f}000Z')
# MSG4-SEVI-MSG15-0100-NA-20190409121243.927000000Z-20190409121300-1329370.nat
# MSG4-SEVI-MSG15-0201-NA-20190409121243.927000000Z.nat
# MSG4-SEVI-MSG15-1234-NA-20190409121243.927000000Z

def load_and_calibrate(filenames, apply_sun_earth_distance_correction, rotate,
                       clip_calib):
    """Load and calibrate data.

    Uses inter-calibration coefficients from Meirink et al.

    Args:
        filenames: List of data files
        apply_sun_earth_distance_correction: If True, apply sun-earth-distance-
            correction to visible channels.
        rotate: Rotate image so that pixel (0, 0) is N-W.
        clip_calib: If True, do not extrapolate calibration coefficients beyond
            the time coverage of the calibration dataset. Instead, clip at the
            boundaries.

    Returns:
        Satpy scene holding calibrated channels
    """
    # Parse filenames
    parser = SEVIRIFilenameParser()
    file_format, info = parser.parse(os.path.basename(filenames[0]))

    calib_coefs = get_calibration(
        platform=info['platform_shortname'],
        time=info['start_time'],
        clip=clip_calib
    )
    scn_ = _create_scene(file_format, filenames, calib_coefs)
    _check_is_seviri_data(scn_)
    _load_bands(scn_, rotate)
    _update_scene_attrs(scn_, {'image_rotated': rotate})

    if not apply_sun_earth_distance_correction:
        remove_sun_earth_distance_correction(scn_)

    return scn_

def _create_scene(file_format, filenames, calib_coefs):
    return Scene(
        reader=file_format,
        filenames=filenames,
        reader_kwargs={'calib_mode': CalibrationData.SATPY_CALIB_MODE.value,
                       'ext_calib_coefs': calib_coefs}
    )

def _check_is_seviri_data(scene):
    if not scene.attrs['sensor'] == {'seviri'}:
        raise ValueError('Not SEVIRI data')

def _load_bands(scene, rotate):
    scene.load(
        BANDNAMES,
        upper_right_corner=_get_upper_right_corner(rotate)
    )

def _get_upper_right_corner(rotation_flag):
    return 'NE' if rotation_flag else 'native'

def _update_scene_attrs(scene, attrs):
    scene.attrs.update(attrs)

def remove_sun_earth_distance_correction(scene):
    """Remove sun-earth-distance correction from visible channels.

    This is required because a downstream CLAAS module (CPP) does not recognize
    the "sun_earth_distance_correction_applied" attribute and applies the
    correction anyway.
    """
    for band in BANDNAMES:
        is_vis = scene[band].attrs['calibration'] == 'reflectance'
        correction_applied = scene[band].attrs.get(
            'sun_earth_distance_correction_applied', False
        )
        if is_vis and correction_applied:
            scene[band] = remove_earthsun_distance_correction(scene[band])

def get_lonlats(dataset):
    """Get lat/lon coordinates."""
    lons, lats = dataset.attrs['area'].get_lonlats()
    lons[np.fabs(lons) > 360] = np.nan
    lats[np.fabs(lats) > 90] = np.nan
    return lons, lats

def get_solar_angles(scene, lons, lats):
    """Compute solar angles.

    Compute angles for each scanline using their acquisition time to account for
    the earth's rotation over the course of one scan.

    Returns:
        Solar azimuth angle, Solar zenith angle in degrees

    """
    suna = np.full(lons.shape, np.nan)
    sunz = np.full(lons.shape, np.nan)
    mean_acq_time = get_mean_acq_time(scene)
    for line, acq_time in enumerate(mean_acq_time.values):
        if np.isnat(acq_time):
            continue
        _, suna_line = get_alt_az(acq_time, lons[line, :], lats[line, :])
        suna_line = np.rad2deg(suna_line)
        suna[line, :] = suna_line
        sunz[line, :] = sun_zenith_angle(acq_time, lons[line, :], lats[line, :])
    return suna, sunz

def get_satellite_angles(dataset, lons, lats):
    """Compute satellite angles.

    Returns:
        Satellite azimuth angle, Satellite zenith angle in degrees

    """
    sat_lon, sat_lat, sat_alt = satpy.utils.get_satpos(dataset)

    # Double check that pyorbital/satpy behave as expected (satpy returning
    # altitude in meters and pyorbital expecting km).
    #
    # if:
    #   1) get_observer_look() gives wrong answer ...
    #   ... for satellite altitude in m. AND
    #   2) get_observer_look() gives correct answer ...
    #   ....  for satellite altitude in km. AND
    #   3) Satellite altitude is m.:
    #    => Satellite alltitude need to be converted to km.
    # else:
    #    => There have been updates to SatPy and this script
    #       need to be modified.
    if not (get_observer_look(0, 0, 36000*1000,
                              datetime.utcnow(), np.array([16]),
                              np.array([58]), np.array([0]))[1] > 30 and
            get_observer_look(0, 0, 36000,
                              datetime.utcnow(), np.array([16]),
                              np.array([58]), np.array([0]))[1] < 23 and
            sat_alt > 38000):
        raise UnexpectedSatpyVersion(
            'Unexpected handling of satellite altitude in pyorbital/'
            'satpy. Conversion to km is probably unneeded and wrong.')

    # Convert altitude from meters to kilometers, as expected by the
    # current version of pyorbital
    sat_alt *= 0.001

    # Compute angles
    sata, satel = get_observer_look(
        sat_lon,
        sat_lat,
        sat_alt,
        dataset.attrs['start_time'],
        lons, lats, 0)
    satz = 90 - satel

    return sata, satz

def set_attrs(scene):
    """Set global and band attributes."""
    # Global
    scene.attrs['platform'] = scene['IR_108'].attrs['platform_name']
    scene.attrs['instrument'] = 'SEVIRI'
    scene.attrs['source'] = "seviri2pps.py"
    scene.attrs['orbit_number'] = 99999
    nowutc = datetime.utcnow()
    scene.attrs['date_created'] = nowutc.strftime("%Y-%m-%dT%H:%M:%SZ")

    # For each band
    for image_num, band in enumerate(BANDNAMES):
        idtag = PPS_TAGNAMES[band]
        scene[band].attrs['id_tag'] = idtag
        scene[band].attrs['description'] = 'SEVIRI ' + str(band)
        scene[band].attrs['wavelength'] = [scene[band].attrs['wavelength'].min,
                                           scene[band].attrs['wavelength'].central,
                                           scene[band].attrs['wavelength'].max]
        if 'sun_earth_distance_correction_factor' not in scene[band].attrs:
            scene[band].attrs['sun_earth_distance_correction_applied'] = False
            scene[band].attrs['sun_earth_distance_correction_factor'] = 1.0
        scene[band].attrs['sun_zenith_angle_correction_applied'] = False
        scene[band].attrs['name'] = "image{:d}".format(image_num)

        # Cosmetics
        for attr in ['orbital_parameters', 'satellite_longitude',
                     'satellite_latitude', 'satellite_altitude',
                     'platform_name', 'sensor', 'georef_offset_corrected']:
            scene[band].attrs.pop(attr, None)

def get_mean_acq_time(scene):
    """Compute mean scanline acquisition time over all bands."""
    dtype = scene['IR_108'].coords['acq_time'].dtype

    # Convert timestamps to float to facilitate averaging. Caveat: NaT is
    # not converted to NaN, but to -9.22E18. So we have to set these elements
    # to NaN manually
    acq_times = []
    for band in BANDNAMES:
        acq_time = scene[band].coords['acq_time'].drop_vars(['acq_time'])
        is_nat = np.isnat(acq_time.values)
        acq_time = acq_time.astype(int).where(np.logical_not(is_nat))
        acq_times.append(acq_time)

    # Compute average over all bands (skip NaNs)
    acq_times = xr.concat(acq_times, 'bands')
    return acq_times.mean(dim='bands', skipna=True).astype(dtype)

def update_coords(scene):
    """Update band coordinates."""
    mean_acq_time = get_mean_acq_time(scene)
    for band in BANDNAMES:
        # Override channel-specific scanline timestamps with mean acquisition
        # time. The differences are not very large and the resulting nc file
        # is much simpler.
        scene[band]['acq_time'] = mean_acq_time

        # Remove area, set lat/lon as coordinates
        scene[band].attrs['coordinates'] = 'lon lat'
        area = scene[band].attrs.pop('area', None)
        if area:
            scene.attrs['area'] = area

        # Add time coordinate to make cfwriter aware that we want 3D data
        scene[band].coords['time'] = scene[band].attrs['start_time']

def add_ancillary_datasets(scene, lons, lats, sunz, satz, azidiff,
                           chunks=(512, 3712)):
    """Add ancillary datasets to the scene.

    Args:
        lons: Longitude coordinates
        lats: Latitude coordinates
        sunz: Solar zenith angle
        satz: Satellite zenith angle
        azidiff: Absolute azimuth difference angle
        chunks: Chunksize

    """
    start_time = scene['IR_108'].attrs['start_time']
    end_time = scene['IR_108'].attrs['end_time']
    angle_coords = scene['IR_108'].coords

    # Latitude
    scene['lat'] = xr.DataArray(
        da.from_array(lats, chunks=chunks),
        dims=['y', 'x'],
        coords={'y': scene['IR_108']['y'], 'x': scene['IR_108']['x']})
    scene['lat'].attrs['long_name'] = 'latitude coordinate'
    scene['lat'].attrs['standard_name'] = 'latitude'
    scene['lat'].attrs['units'] = 'degrees_north'
    scene['lat'].attrs['start_time'] = start_time
    scene['lat'].attrs['end_time'] = end_time

    # Longitude
    scene['lon'] = xr.DataArray(
        da.from_array(lons, chunks=chunks),
        dims=['y', 'x'],
        coords={'y': scene['IR_108']['y'], 'x': scene['IR_108']['x']})
    scene['lon'].attrs['long_name'] = 'longitude coordinate'
    scene['lon'].attrs['standard_name'] = 'longitude'
    scene['lon'].attrs['units'] = 'degrees_east'
    scene['lon'].attrs['start_time'] = start_time
    scene['lon'].attrs['end_time'] = end_time

    # Sunzenith
    scene['sunzenith'] = xr.DataArray(
        da.from_array(sunz[:, :], chunks=chunks),
        dims=['y', 'x'], coords=angle_coords)

    # Satzenith
    scene['satzenith'] = xr.DataArray(
        da.from_array(satz[:, :], chunks=chunks),
        dims=['y', 'x'], coords=angle_coords)

    # Azidiff
    scene['azimuthdiff'] = xr.DataArray(
        da.from_array(azidiff[:, :], chunks=chunks),
        dims=['y', 'x'], coords=angle_coords)

    # Update the attributes
    update_angle_attributes(scene, band=scene['IR_108'])

def add_proj_satpos(scene):
    """Add information on projection and satellite position."""
    orb = scene['IR_108'].attrs['orbital_parameters']

    # Area extent
    try:
        # Traditionally a, b was included in proj_dict
        param_a = scene.attrs['area'].proj_dict['a']
        param_b = scene.attrs['area'].proj_dict['b']
    except KeyError:
        # But sometimes b is missing:
        param_a = scene.attrs['area'].crs.ellipsoid.semi_major_metre
        param_b = scene.attrs['area'].crs.ellipsoid.semi_minor_metre

    scene.attrs.update({
        'projection': 'geos',
        'projection_semi_major_axis': param_a,
        'projection_semi_minor_axis': param_b,
        'projection_longitude': orb['projection_longitude'],
        'projection_latitude': orb['projection_latitude'],
        'projection_altitude': orb['projection_altitude']
    })
    scene['projection_area_extent'] = xr.DataArray(
        np.array([scene.attrs['area'].area_extent]),
        dims=('time', 'corners'),
        attrs={'units': 'meters',
               'long_name': 'Projection area extent',
               'comment': 'Area extent (lower left x, lower left y, '
                          'upper right x, upper right y) in projection '
                          'coordinates'}
    )
    scene['georef_offset_corrected'] = xr.DataArray(
        np.array([scene['IR_108'].attrs['georef_offset_corrected']],
                 dtype='int8'),
        dims=('time', ),
        attrs={'long_name': 'Georeferencing offset correction flag',
               'flag_values': np.array([0, 1], dtype='int8'),
               'flag_meanings': 'georef_offset_present georef_offset_corrected',
               'comment': 'Until December 2017, SEVIRI L1.5 data is shifted '
                          'by 1.5km SSP North and West against the nominal '
                          'GEOS projection. Since December 2017 this offset '
                          'has been corrected by EUMETSAT. This flag indicates '
                          'if the correction has been applied. If not, the area '
                          'extent is adjusted so that the projection always '
                          'matches the data.'}
    )

    # Satellite position
    scene['satellite_longitude'] = xr.DataArray(
        [orb['satellite_actual_longitude']],
        dims=('time',),
        attrs={'units': 'degree_east',
               'long_name': 'Actual satellite longitude',
               'comment': 'Not to be confused with projection longitude'}
    )
    scene['satellite_latitude'] = xr.DataArray(
        [orb['satellite_actual_latitude']],
        dims=('time',),
        attrs={'units': 'degree_north',
               'long_name': 'Actual satellite latitude',
               'comment': 'Not to be confused with projection latitude'}
    )
    scene['satellite_altitude'] = xr.DataArray(
        [orb['satellite_actual_altitude']],
        dims=('time',),
        attrs={'units': 'meters',
               'long_name': 'Actual satellite altitude',
               'comment': 'Relative and perpendicular to the surface of the '
                          'earth ellipsoid. Not to be confused with projection '
                          'altitude.'}
    )

def get_encoding_seviri(scene):
    """Get netcdf encoding for all datasets."""
    # Bands
    chunks = (1, 512, 3712)
    encoding = get_encoding(scene,
                            bandnames=BANDNAMES,
                            pps_tagnames=PPS_TAGNAMES,
                            chunks=chunks)

    # Time
    acq_units = scene.attrs['start_time'].strftime(
        'milliseconds since %Y-%m-%d %H:%M')
    encoding['acq_time'] = {'units': acq_units,
                            'calendar': 'standard',
                            '_FillValue': -9999.0}
    encoding['time'] = {'units': 'days since 2004-01-01 00:00',
                        'calendar': 'standard',
                        '_FillValue': None,
                        'chunksizes': [1]}

    return encoding

def get_header_attrs(scene):
    """Get global netcdf attributes."""
    header_attrs = scene.attrs.copy()
    for key in ['sensor', 'area']:
        header_attrs.pop(key, None)
    return header_attrs

def _drop_seconds_milliseconds(dtime):
    return dtime.replace(second=0, microsecond=0)

def set_nominal_scan_time(dataset):
    """Set time attributes in the dataset to the nominal scan time."""
    dataset = dataset.copy()
    start_time = _drop_seconds_milliseconds(
        dataset.attrs['start_time'])
    end_time = start_time + timedelta(minutes=15)
    dataset.attrs['start_time'] = start_time
    dataset.attrs['end_time'] = end_time
    return dataset

class SEVIRIFilenameParser:
    """SEVIRI filename parser."""

    default_formats = [
        {'name': 'seviri_l1b_hrit',
         'pattern': HRIT_FILE_PATTERN,
         'full_match': True},
        {'name': 'seviri_l1b_native',
         'pattern': NATIVE_FILE_PATTERN,
         'full_match': False},
    ]

    def __init__(self, formats=None):
        self.formats = formats or self.default_formats
        self.format_names = [fmt['name'] for fmt in self.formats]

    def parse(self, filename):
        """Parse the given filename.

        Returns:
            File format, filename info
        """
        for fmt in self.formats:
            parser = Parser(fmt['pattern'])
            try:
                info = parser.parse(filename, full_match=fmt['full_match'])
                break
            except ValueError:
                continue
        else:
            raise ValueError(
                'Filename {} doesn\'t match any of the supported '
                'formats {}.'.format(filename, self.format_names)

            )
        return fmt['name'], self._postproc(fmt['name'], info)

    def _postproc(self, format_name, info):
        """Postprocess filename info."""
        if format_name == 'seviri_l1b_native':
            return self._postproc_native(info)
        elif format_name == 'seviri_l1b_hrit':
            return self._postproc_hrit(info)
        else:
            raise NotImplementedError

    def _postproc_native(self, info):
        """Postprocess Native filename info."""
        # Only end time available, derive start time.
        end_time = info['end_time']
        quarter = end_time.minute // 15
        info['start_time'] = end_time.replace(
            minute=quarter * 15,
            second=0,
            microsecond=0
        )
        return info

    def _postproc_hrit(self, parsed):
        """Postprocess HRIT filename info."""
        return parsed

def process_one_scan(tslot_files, out_path, rotate=True, engine='h5netcdf',
                     use_nominal_time_in_filename=False,
                     apply_sun_earth_distance_correction=True,
                     clip_calib=False):
    """Make level 1c files in PPS-format."""
    for fname in tslot_files:
        if not os.path.isfile(fname):
            raise FileNotFoundError('No such file: {}'.format(fname))

    tic = time.time()
    scn_ = load_and_calibrate(
        tslot_files,
        apply_sun_earth_distance_correction=apply_sun_earth_distance_correction,
        rotate=rotate,
        clip_calib=clip_calib
    )

    # Find lat/lon data
    lons, lats = get_lonlats(scn_['IR_108'])

    # Compute angles
    suna, sunz = get_solar_angles(scn_, lons=lons, lats=lats)
    sata, satz = get_satellite_angles(scn_['IR_108'], lons=lons, lats=lats)
    azidiff = make_azidiff_angle(sata, suna)

    # Update coordinates
    update_coords(scn_)

    # Add ancillary datasets to the scene
    add_ancillary_datasets(scn_, lons=lons, lats=lats, sunz=sunz, satz=satz,
                           azidiff=azidiff)
    add_proj_satpos(scn_)

    # Set attributes. This changes SEVIRI band names to PPS band names.
    set_attrs(scn_)

    # Write datasets to netcdf
    ir108_for_filename = scn_['IR_108']
    if use_nominal_time_in_filename:
        ir108_for_filename = set_nominal_scan_time(ir108_for_filename)
    filename = compose_filename(
        scene=scn_,
        out_path=out_path,
        instrument='seviri',
        band=ir108_for_filename
    )
    scn_.save_datasets(writer='cf',
                       filename=filename,
                       header_attrs=get_header_attrs(scn_),
                       engine=engine,
                       encoding=get_encoding_seviri(scn_),
                       unlimited_dims=['time'],
                       include_lonlats=False,
                       pretty=True,
                       flatten_attrs=True,
                       exclude_attrs=['raw_metadata'])
    print("Saved file {:s} after {:3.1f} seconds".format(
        os.path.basename(filename),
        time.time()-tic))  # About 40 seconds
    return filename

def process_all_scans_in_dname(dname, out_path, ok_dates=None, rotate=False):
    """Make level 1c files for all files in directory dname."""
    parser = Parser(HRIT_FILE_PATTERN)
    fl_ = glob(os.path.join(dname, globify(HRIT_FILE_PATTERN)))
    dates = [parser.parse(os.path.basename(p))['start_time'] for p in fl_]
    unique_dates = np.unique(dates).tolist()
    for uqdate in unique_dates:
        date_formated = uqdate.strftime("%Y%m%d%H%M")
        if ok_dates is not None and date_formated not in ok_dates.keys():
            print("Skipping date {date}".format(date=date_formated))
            continue
        # Every hour only:
        # if uqdate.minute != 0:
        #    continue
        tslot_files = [f for f in fl_ if parser.parse(
            os.path.basename(f))['start_time'] == uqdate]
        try:
            process_one_scan(tslot_files, out_path, rotate=rotate)
        except Exception:
            pass


"""
kernel instance module

instantiated and compiled version of a kernel

this module defines the abstract base class; much of the kernel is backend specific, however

"""

import numpy as np

class AbstractKernelInstance(object):
    """
    wraps a declaration and its derived backend-specific kernel object
    """
    def __init__(self, context, declaration, kernel):
        self.context = context
        self.declaration = declaration
        self.kernel = kernel

    def __call__(self, *args):
        raise NotImplementedError()

    def array_data(self, array):
        raise NotImplementedError()

    def check_arguments(self, args):
        """runtime argument validity checking"""
        #zip args and declaration
        for v_arg, d_arg in zip(args, self.declaration.arguments.itervalues()): #arguments iterated in order they were declared
            if d_arg.is_array:
                #check dtype
                assert(v_arg.dtype == d_arg.dtype)
                #check dims
                assert(len(v_arg.shape) == d_arg.ndim)
                #check size
                for i, (v_size, d_axis) in enumerate( zip(v_arg.shape, d_arg.shape)):
                    if d_axis.is_constant:
                        if d_arg.boundary=='padded':
                            stencil = self.declaration.get_stencil(d_arg.stencil)
                            assert(v_size == d_axis.size + stencil.total_padding[i])
                        else:
                            assert(v_size == d_axis.size)

    def mine_arguments(self, args):
        """
        mine the argument list for runtime-arguments

        abstract this stuff away in decl?
        """

        from collections import OrderedDict
        _size_arguments = {}    #temp dict

        #zip args and declaration
        for v_arg, d_arg in zip(args, self.declaration.arguments.itervalues()): #arguments iterated in order they were declared
            #mine shape arguments
            for i, (v_axis, d_axis) in enumerate(zip(v_arg.shape, d_arg.shape)):
                if d_axis.is_variable:
                    cast = np.uint32
                    if d_arg.boundary=='padded':
                        stencil = self.declaration.get_stencil(d_arg.stencil)
                        padding = stencil.total_padding
                        _size_arguments[d_axis.identifier] = cast(v_axis) - padding[i]
                    else:
                        _size_arguments[d_axis.identifier] = cast(v_axis)

        #reorder size arguments in order as specified by size_arguments set (same as used by code generator to create C signature)
        size_arguments = OrderedDict()
        for axis in self.declaration.size_variables:
            size_arguments[axis] = _size_arguments[axis]

        return size_arguments

    def base_arguments(self, args):
        """
        given complete list of input/output args
        build list of c-ready information
        """

        def base(decl, value):
            """arrays are passed in in background dependent way. scalars are passed in as numpy types"""
            if decl.is_array:
                return self.array_data(value)
            if decl.is_scalar:
                cast = getattr(np, decl.dtype)
                return cast(value)

        return [base(decl, value) for  value, decl in  zip(args, self.declaration.arguments.itervalues())]

    def output_arguments(self, args, size_arguments):
        """
        allocate missing output args,
        and build list of returned objects
        """
        def substitute(axis):
            return size_arguments.get( axis.identifier, axis.size)

        output_arguments = []    #list of things to return from kernel

        outputs = iter(self.declaration.outputs)
        #simple copy over supplied output args to output list; consume a matching number of output declarations
        for value, decl in zip(args[len(self.declaration.inputs):], outputs):
            output_arguments.append(value)
        #allocate the missing arguments
        for output in outputs:
            shape = tuple(int(substitute(axis)) for axis in output.shape)  #deref to size
            if output.default in self.declaration.input_identifiers:
                #init output with copy of input
                index = self.declaration.input_identifiers.index(output.default)
                value = self.context.array(args[index])
            else:
                #allocate output, and fill with default, if available
                if output.boundary=='padded':
                    #adjust shape to padding of stencil
                    stencil = self.declaration.get_stencil(output.stencil)
                    shape = tuple(s+p for s,p in zip(shape, stencil.total_padding))
                #allocate
                if output.default:
                    value = self.context.filled(shape, output.dtype, output.default)
                else:
                    value = self.context.empty(shape, output.dtype)
            #append to list of calling and return arguments
            args.append(value)
            output_arguments.append(value)

        return args, output_arguments

    def __call__(self, *args, **kwargs):
        """
        invoke the kernel
        this involves
         - checking arguments for correctness
         - collecting runtime size arguments from the argument list
         - allocating missing output arguments
         - performing grid and block calculations
         - invoking the backend-specific kernel
        and
        """
        args = list(args)

        #check arguments for compatibility with the declaration
        self.check_arguments(args)

        #pull runtime variables from the argument list
        size_arguments = self.mine_arguments(args)
        for s in self.declaration.size_variables:
            if s in kwargs:
                size_arguments[s] = kwargs.pop(s)

        #allocate output arguments; extend args list
        args, output_args = self.output_arguments(args, size_arguments)

        #build total output buffer
        arg_buffer = (
            self.base_arguments(args) +
            [np.uint32(s) for s in size_arguments.values()])

        #invoke the kernel; backend specific; give size arguments to be able to do thread/block logic
        self.invoke(arg_buffer, size_arguments)

        #return the output args
        if len(output_args)==1:
            return output_args[0]
        else:
            return tuple(output_args)


from c_Controllers.profesor import profesorController
class Menuprofesor:
    def __init__(self):
        self.profesor=profesorController()

    def menu_profesor(self):
        while True:
            print('''
                Menú profesor
                1) Listar Todos los profesores.
                2) Listar mediante una condicion
                3) Agregar profesor
                4) Editar profesor
                5) Eliminar profesor
                6) Ir al MENU PRINCIPAL
            \n''')
            opcion = input('Ingresa el n° : ')
            if opcion == '1':
                self.profesor.list_profesores()
            elif opcion == '2':
                self.profesor.lista_profesor_multiple_conditions()
            elif opcion == '3':
                self.profesor.insert_profesor_data()
            elif opcion == '4':
                self.profesor.update_profesor()
            elif opcion == '5':
                self.profesor.delete_profesor()
            elif opcion == '6':
                break
            else:
                print('No escogiste una opción valida')


from time import time
from zlib import adler32, crc32

c = 'test'
start = time()
b=(str(c) + 'A' * 10000000).encode()
for i in range(2): c = adler32(b)
print ('Adler32 benchmark: %3f' % (time() - start), 'seconds')

start = time()
for i in range(256*256*256): c = crc32(str(c) + 'A' * 1000)
print ('CRC32 benchmark: %3.1f' % (time() - start), 'seconds')


# Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""FAN Neck module."""
import warnings
import torch
import torch.nn as nn
import torch.nn.functional as F
from mmcv.cnn import ConvModule

def resize(input,   # pylint: disable=W0622
           size=None,
           scale_factor=None,
           mode='nearest',
           align_corners=None,
           warning=True):
    """ Resize Function."""
    if warning:
        if size is not None and align_corners:
            input_h, input_w = tuple(int(x) for x in input.shape[2:])
            output_h, output_w = tuple(int(x) for x in size)
            if output_h > input_h or output_w > output_h:   # pylint: disable=R0916
                if ((output_h > 1 and output_w > 1 and input_h > 1 and  # pylint: disable=R0916
                     input_w > 1) and (output_h - 1) % (input_h - 1) and (output_w - 1) % (input_w - 1)):  # pylint: disable=R0916
                    warnings.warn(
                        f'When align_corners={align_corners}, '
                        'the output would more aligned if '
                        f'input size {(input_h, input_w)} is `x+1` and '
                        f'out size {(output_h, output_w)} is `nx+1`')
    if isinstance(size, torch.Size):
        size = tuple(int(x) for x in size)

    return F.interpolate(input, size, scale_factor, mode, align_corners)

class MLP(nn.Module):
    """Linear Embedding."""

    def __init__(self, input_dim=2048, embed_dim=768, export=False):
        """Init."""
        super().__init__()
        self.proj = nn.Linear(input_dim, embed_dim)
        self.export = export

    def forward(self, x):
        """Forward."""
        if self.export:
            _, C, H, W = x.shape
            x = x.view(-1, C, H * W).transpose(1, 2)
        else:
            x = x.flatten(2).transpose(1, 2)
        x = self.proj(x)
        return x

class FANNeck(nn.Module):
    """
    SegFormer: Simple and Efficient Design for Semantic Segmentation with Transformers
    """

    def __init__(self, in_channels, feature_strides=[4, 8, 16, 32], dropout_ratio=0.1, embedding_dim=768, export=False, **kwargs):
        """Init Module."""
        super(FANNeck, self).__init__()
        self.in_channels = in_channels
        assert len(feature_strides) == len(self.in_channels), "The number of feature strides:{} should be equal to number of channels: {}".format(feature_strides, len(self.in_channels))
        assert min(feature_strides) == feature_strides[0], "Minimum of feature strides is not supported."
        self.feature_strides = feature_strides
        self.export = export
        if dropout_ratio > 0:
            self.dropout = nn.Dropout2d(dropout_ratio)
        c1_in_channels, c2_in_channels, c3_in_channels, c4_in_channels = self.in_channels

        self.linear_c4 = MLP(input_dim=c4_in_channels, embed_dim=embedding_dim, export=self.export)
        self.linear_c3 = MLP(input_dim=c3_in_channels, embed_dim=embedding_dim, export=self.export)
        self.linear_c2 = MLP(input_dim=c2_in_channels, embed_dim=embedding_dim, export=self.export)
        self.linear_c1 = MLP(input_dim=c1_in_channels, embed_dim=embedding_dim, export=self.export)

        self.linear_fuse = ConvModule(
            in_channels=embedding_dim * 4,
            out_channels=embedding_dim,
            kernel_size=1,
            norm_cfg=dict(type='SyncBN', requires_grad=True)
        )
        self.out_channels = embedding_dim

    def forward(self, x):
        """Forward."""
        c1, c2, c3, c4 = x
        neck_out_size = c1.size()[2:]
        # MLP decoder on C1-C4 #
        n, _, _, _ = c4.shape

        _c4 = self.linear_c4(c4).permute(0, 2, 1).reshape(n, -1, c4.shape[2], c4.shape[3])
        _c4 = resize(_c4, size=neck_out_size, mode='bilinear', align_corners=False)

        _c2 = self.linear_c2(c2).permute(0, 2, 1).reshape(n, -1, c2.shape[2], c2.shape[3])
        _c2 = resize(_c2, size=neck_out_size, mode='bilinear', align_corners=False)

        _c1 = self.linear_c1(c1).permute(0, 2, 1).reshape(n, -1, c1.shape[2], c1.shape[3])

        # disable autocast to address "nan" error when AMP training
        with torch.cuda.amp.autocast(enabled=False):
            _c3 = self.linear_c3(c3).permute(0, 2, 1).reshape(n, -1, c3.shape[2], c3.shape[3])
            _c3 = resize(_c3, size=neck_out_size, mode='bilinear', align_corners=False)
            _c = self.linear_fuse(torch.cat([_c4, _c3, _c2, _c1], dim=1))

        x = self.dropout(_c)
        return x


from time import sleep
print('{:=^40}'.format(' PROGRESSÃO ARITMÉTICA '))
print('{:=^40}'.format(' 10 TERMOS DE UMA PA '))
t=int(input('\nDigite o 1º termo da PA: '))
r=int(input('Digite a razão da PA: '))
termos=int(input('Quantos termos você precisa? ')) # Para poder escolher a quantidade de termos
# termos=10
n=t+termos*r
s=0
for c in range(t,n,r):
    print(c, end=' → ')
    sleep(.05)
print('ACABOU')

# s + r

# Exercício Python 051: Desenvolva um programa que leia o primeiro termo e a razão de uma PA. No final, mostre os 10 primeiros termos dessa progressão.


#************************************************************
# code from：
#  https://github.com/tamasino52/UNETR/blob/main/unetr.py
#
# # Reference Links:
# https://github.com/Project-MONAI/research-contributions/blob/main/UNETR/BTCV/networks/unetr.py
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class SingleDeconv3DBlock(nn.Module):
    '''
    使用转置卷积来实现上采样
    '''
    def __init__(self, in_planes, out_planes):
        super().__init__()
        self.block = nn.ConvTranspose3d(in_planes, out_planes, kernel_size=2, stride=2, padding=0, output_padding=0)

    def forward(self, x):
        return self.block(x)

class SingleConv3DBlock(nn.Module):
    def __init__(self, in_planes, out_planes, kernel_size):
        super().__init__()
        self.block = nn.Conv3d(in_planes, out_planes, kernel_size=kernel_size, stride=1,
                               padding=((kernel_size - 1) // 2))

    def forward(self, x):
        return self.block(x)

class Conv3DBlock(nn.Module):
    '''
    decoder的三维卷积模块
    conv3x3x3,BN,Relu
    '''
    def __init__(self, in_planes, out_planes, kernel_size=3):
        super().__init__()
        self.block = nn.Sequential(
            SingleConv3DBlock(in_planes, out_planes, kernel_size),
            nn.BatchNorm3d(out_planes),
            nn.ReLU(True)
        )

    def forward(self, x):
        return self.block(x)

class Deconv3DBlock(nn.Module):
    '''
    反卷积上采样模块
    deconv2x2x2,conv3x3x3,BN,Relu
    '''
    def __init__(self, in_planes, out_planes, kernel_size=3):
        super().__init__()
        self.block = nn.Sequential(
            SingleDeconv3DBlock(in_planes, out_planes),
            SingleConv3DBlock(out_planes, out_planes, kernel_size),
            nn.BatchNorm3d(out_planes),
            nn.ReLU(True)
        )

    def forward(self, x):
        return self.block(x)

class SelfAttention(nn.Module):
    '''
    transformer结构的核心模块:自注意力模块
    学习Wq,Wk,Wv矩阵
    # 输入和输出是相同的的尺寸[B,Seq_dim,embded_dim]
    '''
    def __init__(self, num_heads, embed_dim, dropout):
        super().__init__()
        self.num_attention_heads = num_heads
        self.attention_head_size = int(embed_dim / num_heads)
        self.all_head_size = self.num_attention_heads * self.attention_head_size

        #query,key,value 具体实现是一个线性层(全量就层) 输入维度是K/n,输出维度是K
        self.query = nn.Linear(embed_dim, self.all_head_size)
        self.key = nn.Linear(embed_dim, self.all_head_size)
        self.value = nn.Linear(embed_dim, self.all_head_size)

        self.out = nn.Linear(embed_dim, embed_dim)
        self.attn_dropout = nn.Dropout(dropout)
        self.proj_dropout = nn.Dropout(dropout)

        self.softmax = nn.Softmax(dim=-1)

        self.vis = False

    def transpose_for_scores(self, x):
        #x.shape=[1,512,768]
        # reshape tensor 到需要的维度[B,embded_dim,heads,head_size] torch.Size([1, 512, 12, 64])
        new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
        x = x.view(*new_x_shape)
        return x.permute(0, 2, 1, 3)

    def forward(self, hidden_states):
        mixed_query_layer = self.query(hidden_states)
        mixed_key_layer = self.key(hidden_states)
        mixed_value_layer = self.value(hidden_states)

        query_layer = self.transpose_for_scores(mixed_query_layer)
        key_layer = self.transpose_for_scores(mixed_key_layer)
        value_layer = self.transpose_for_scores(mixed_value_layer)

        # Q 和 K 计算出 scores，然后将 scores 和 V 相乘，得到每个patch的context vector

        #1.SA(z) = Softmax( qk> √Ch )v,计算出 scores
        attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) # torch.Size([1, 12, 512, 512])
        attention_scores = attention_scores / math.sqrt(self.attention_head_size)
        attention_probs = self.softmax(attention_scores)

        weights = attention_probs if self.vis else None
        attention_probs = self.attn_dropout(attention_probs)
        #2.scores 和 V 相乘
        context_layer = torch.matmul(attention_probs, value_layer)
        context_layer = context_layer.permute(0, 2, 1, 3).contiguous()#torch.Size([1, 12, 512, 64])
        new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)#torch.Size([1, 512, 768])
        context_layer = context_layer.view(*new_context_layer_shape)
        # 最后的一个线性输出层
        attention_output = self.out(context_layer)
        #加了一个dropout层
        attention_output = self.proj_dropout(attention_output)
        return attention_output, weights

class Mlp(nn.Module):
    '''
    MLP 层
    采用高斯误差线性单元激活函数GELU
    zi = MLP(Norm(z0i)) + z0i,
    '''
    def __init__(self, in_features, act_layer=nn.GELU, drop=0.):
        super().__init__()
        self.fc1 = nn.Linear(in_features, in_features)
        self.act = act_layer()
        self.drop = nn.Dropout(drop)

    def forward(self, x):
        x = self.fc1()
        x = self.act(x)
        x = self.drop(x)
        return x

class PositionwiseFeedForward(nn.Module):
    '''
    位置级前馈网络
    除了注意子层外,我们的编码器和解码器中的每个层都包含一个完全连接的前馈网络.
    它分别和相同地应用于每个位置。这由两个线性变换组成.中间有一个ReLU激活。
    FFN(x) = max(0, xW1 + b1)W2 + b2 (2)
    '''
    def __init__(self, d_model=786, d_ff=2048, dropout=0.1):
        super().__init__()
        # Torch linears have a `b` by default.
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        #Residual Dropout
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

class Embeddings(nn.Module):
    '''
    embedded patches

    '''
    def __init__(self, input_dim, embed_dim, cube_size, patch_size, dropout):
        super().__init__()
        #计算有多少个patch
        self.n_patches = int((cube_size[0] * cube_size[1] * cube_size[2]) / (patch_size * patch_size * patch_size))
        # patch的大小
        self.patch_size = patch_size
        # 嵌入的尺寸大小，默认768
        self.embed_dim = embed_dim
        #使用3D卷积计算patch embedding
        # 在NLP中语言序列是1D的序列使用朋友torch中的nn.Embedding()
        self.patch_embeddings = nn.Conv3d(in_channels=input_dim, out_channels=embed_dim,
                                          kernel_size=patch_size, stride=patch_size)
        # 设置一个可以学习的嵌入位置参数
        #将一个固定不可训练的tensor转换成可以训练的类型parameter，并将这个parameter绑定到这个module里面(net.parameter()中就有这个绑定的parameter，所以在参数优化的时候可以进行优化的)，
        # 所以经过类型转换这个self.position_embeddings变成了模型的一部分，成为了模型中根据训练可以改动的参数了。
        # 使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化
        self.position_embeddings = nn.Parameter(torch.zeros(1, self.n_patches, embed_dim))
        #dropout 层
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        #[1,4,128,128,128]->[1,768,8,8,8]
        x = self.patch_embeddings(x)
        #从dim=2开始展平->[1,768,512]
        x = x.flatten(2)
        x = x.transpose(-1, -2) #[1,512,768]
        # 直接加上位置信息
        embeddings = x + self.position_embeddings
        embeddings = self.dropout(embeddings)
        return embeddings

class TransformerBlock(nn.Module):
    '''
    可重复的transformer block
    Norm->MSA->Norm->MLP
    '''
    def __init__(self, embed_dim, num_heads, dropout, cube_size, patch_size):
        super().__init__()
        #归一化，在一个样本上做归一化操作这里是laerNorm 而不是BatchNorm
        self.attention_norm = nn.LayerNorm(embed_dim, eps=1e-6)
        self.mlp_norm = nn.LayerNorm(embed_dim, eps=1e-6)
        #mlp dim
        self.mlp_dim = int((cube_size[0] * cube_size[1] * cube_size[2]) / (patch_size * patch_size * patch_size))
        self.mlp = PositionwiseFeedForward(embed_dim, 2048)
        self.attn = SelfAttention(num_heads, embed_dim, dropout)

    def forward(self, x):
        h = x
        #1.NORM
        x = self.attention_norm(x)
        #2.MSA
        x, weights = self.attn(x)
        # 残差链接
        x = x + h
        h = x
        #3.MLP
        x = self.mlp_norm(x)
        x = self.mlp(x)
        #残差链接
        x = x + h
        return x, weights

class Transformer(nn.Module):
    """
    tansformer as the encoder:

    Args:
        input_dim:=4(MRI数据,多channel)
            输入数据的channel
        embed_dim:=768
            embedding 的尺寸
        cube_size:
            体数据的尺寸
        patch_size:=16
            补丁的个数
        num_heads:=12
            有多少个Multi-Head
        num_layers:
            layer的数目对应num_heads

        dropout:0.1
            随机dropout的概率
        extract_layers:=[3,6,9,12]
            提取特征的层

    """
    def __init__(self, input_dim, embed_dim, cube_size, patch_size, num_heads, num_layers, dropout, extract_layers):
        super().__init__()
        self.embeddings = Embeddings(input_dim, embed_dim, cube_size, patch_size, dropout)
        self.layer = nn.ModuleList()
        self.encoder_norm = nn.LayerNorm(embed_dim, eps=1e-6)
        self.extract_layers = extract_layers
        for _ in range(num_layers):
            layer = TransformerBlock(embed_dim, num_heads, dropout, cube_size, patch_size)
            self.layer.append(copy.deepcopy(layer))

    def forward(self, x):
        extract_layers = []
        hidden_states = self.embeddings(x)

        for depth, layer_block in enumerate(self.layer):
            hidden_states, _ = layer_block(hidden_states)
            if depth + 1 in self.extract_layers:
                extract_layers.append(hidden_states)

        return extract_layers

class UNETR(nn.Module):
    def __init__(self, img_shape=(128, 128, 128), in_channels=4, out_channels=3, embed_dim=768, patch_size=16, num_heads=12, dropout=0.1):
        super().__init__()
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.embed_dim = embed_dim
        self.img_shape = img_shape
        self.patch_size = patch_size
        self.num_heads = num_heads
        self.dropout = dropout
        self.num_layers = 12
        self.ext_layers = [3, 6, 9, 12]

        self.patch_dim = [int(x / patch_size) for x in img_shape]

        # Transformer Encoder
        self.transformer = \
            Transformer(
                in_channels,
                embed_dim,
                img_shape,
                patch_size,
                num_heads,
                self.num_layers,
                dropout,
                self.ext_layers
            )

        # U-Net Decoder
        self.decoder0 = \
            nn.Sequential(
                Conv3DBlock(in_channels, 32, 3),
                Conv3DBlock(32, 64, 3)
            )

        self.decoder3 = \
            nn.Sequential(
                Deconv3DBlock(embed_dim, 512),
                Deconv3DBlock(512, 256),
                Deconv3DBlock(256, 128)
            )

        self.decoder6 = \
            nn.Sequential(
                Deconv3DBlock(embed_dim, 512),
                Deconv3DBlock(512, 256),
            )

        self.decoder9 = \
            Deconv3DBlock(embed_dim, 512)

        self.decoder12_upsampler = \
            SingleDeconv3DBlock(embed_dim, 512)

        self.decoder9_upsampler = \
            nn.Sequential(
                Conv3DBlock(1024, 512),
                Conv3DBlock(512, 512),
                #Conv3DBlock(512, 512),
                SingleDeconv3DBlock(512, 256)
            )

        self.decoder6_upsampler = \
            nn.Sequential(
                Conv3DBlock(512, 256),
                Conv3DBlock(256, 256),
                SingleDeconv3DBlock(256, 128)
            )

        self.decoder3_upsampler = \
            nn.Sequential(
                Conv3DBlock(256, 128),
                Conv3DBlock(128, 128),
                SingleDeconv3DBlock(128, 64)
            )

        self.decoder0_header = \
            nn.Sequential(
                Conv3DBlock(128, 64),
                Conv3DBlock(64, 64),
                SingleConv3DBlock(64, out_channels, 1)
            )

    def forward(self, x):
        z = self.transformer(x)#z=[4,1,512,768]
        z0, z3, z6, z9, z12 = x, *z
        #[1,512,768]->[1,768,8,8,8]
        z3 = z3.transpose(-1, -2).view(-1, self.embed_dim, *self.patch_dim)
        z6 = z6.transpose(-1, -2).view(-1, self.embed_dim, *self.patch_dim)
        z9 = z9.transpose(-1, -2).view(-1, self.embed_dim, *self.patch_dim)
        z12 = z12.transpose(-1, -2).view(-1, self.embed_dim, *self.patch_dim)

        z12 = self.decoder12_upsampler(z12)
        z9 = self.decoder9(z9)
        z9 = self.decoder9_upsampler(torch.cat([z9, z12], dim=1))
        z6 = self.decoder6(z6)
        z6 = self.decoder6_upsampler(torch.cat([z6, z9], dim=1))
        z3 = self.decoder3(z3)
        z3 = self.decoder3_upsampler(torch.cat([z3, z6], dim=1))
        z0 = self.decoder0(z0)
        output = self.decoder0_header(torch.cat([z0, z3], dim=1))
        return output

# if __name__ == "__main__":
#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#     x = torch.Tensor(1, 4, 128, 128, 128)
#     x.to(device)
#     print("x size: {}".format(x.size()))

#     model = UNETR()

#     out = model(x)
#     print("out size: {}".format(out.size()))

if __name__ == '__main__':
    # 使用torchsummary时报错AttributeError: ‘list‘ object has no attribute ‘size‘
    # https://blog.csdn.net/huanjin_w/article/details/110858744
    # 最新版本的 torch-summary 1.4.5解决了这个问题
    # torch-summary已经重命名为torchinfo，建议使用torchinfo
    #https://pypi.org/project/torch-summary/
    #https://pypi.org/project/torchinfo/
    #from torchsummary import summary
    from torchinfo import summary
    unetr = UNETR()
    #print(unetr)
    unetr.to(torch.device("cuda:0" if torch.cuda.is_available() else "cpu"))
    summary(unetr,(1,4,128,128,128))


"""Tentative prolongator."""

import numpy as np
from scipy.sparse import isspmatrix_csr, bsr_matrix
from pyamg import amg_core

def fit_candidates(AggOp, B, tol=1e-10):
    """Fit near-nullspace candidates to form the tentative prolongator.

    Parameters
    ----------
    AggOp : csr_matrix
        Describes the sparsity pattern of the tentative prolongator.
        Has dimension (#blocks, #aggregates)
    B : array
        The near-nullspace candidates stored in column-wise fashion.
        Has dimension (#blocks * blocksize, #candidates)
    tol : scalar
        Threshold for eliminating local basis functions.
        If after orthogonalization a local basis function Q[:, j] is small,
        i.e. ||Q[:, j]|| < tol, then Q[:, j] is set to zero.

    Returns
    -------
    (Q, R) : (bsr_matrix, array)
        The tentative prolongator Q is a sparse block matrix with dimensions
        (#blocks * blocksize, #aggregates * #candidates) formed by dense blocks
        of size (blocksize, #candidates).  The coarse level candidates are
        stored in R which has dimensions (#aggregates * #candidates,
        #candidates).

    See Also
    --------
    amg_core.fit_candidates

    Notes
    -----
        Assuming that each row of AggOp contains exactly one non-zero entry,
        i.e. all unknowns belong to an aggregate, then Q and R satisfy the
        relationship B = Q*R.  In other words, the near-nullspace candidates
        are represented exactly by the tentative prolongator.

        If AggOp contains rows with no non-zero entries, then the range of the
        tentative prolongator will not include those degrees of freedom. This
        situation is illustrated in the examples below.

    References
    ----------
    .. [1] Vanek, P. and Mandel, J. and Brezina, M.,
       "Algebraic Multigrid by Smoothed Aggregation for
       Second and Fourth Order Elliptic Problems",
       Computing, vol. 56, no. 3, pp. 179--196, 1996.
       http://citeseer.ist.psu.edu/vanek96algebraic.html

    Examples
    --------
    >>> from scipy.sparse import csr_matrix
    >>> from pyamg.aggregation.tentative import fit_candidates
    >>> # four nodes divided into two aggregates
    ... AggOp = csr_matrix( [[1, 0],
    ...                      [1, 0],
    ...                      [0, 1],
    ...                      [0, 1]] )
    >>> # B contains one candidate, the constant vector
    ... B = [[1],
    ...      [1],
    ...      [1],
    ...      [1]]
    >>> Q, R = fit_candidates(AggOp, B)
    >>> Q.toarray()
    array([[0.70710678, 0.        ],
           [0.70710678, 0.        ],
           [0.        , 0.70710678],
           [0.        , 0.70710678]])
    >>> R
    array([[1.41421356],
           [1.41421356]])
    >>> # Two candidates, the constant vector and a linear function
    ... B = [[1, 0],
    ...      [1, 1],
    ...      [1, 2],
    ...      [1, 3]]
    >>> Q, R = fit_candidates(AggOp, B)
    >>> Q.toarray()
    array([[ 0.70710678, -0.70710678,  0.        ,  0.        ],
           [ 0.70710678,  0.70710678,  0.        ,  0.        ],
           [ 0.        ,  0.        ,  0.70710678, -0.70710678],
           [ 0.        ,  0.        ,  0.70710678,  0.70710678]])
    >>> R
    array([[1.41421356, 0.70710678],
           [0.        , 0.70710678],
           [1.41421356, 3.53553391],
           [0.        , 0.70710678]])
    >>> # aggregation excludes the third node
    ... AggOp = csr_matrix( [[1, 0],
    ...                      [1, 0],
    ...                      [0, 0],
    ...                      [0, 1]] )
    >>> B = [[1],
    ...      [1],
    ...      [1],
    ...      [1]]
    >>> Q, R = fit_candidates(AggOp, B)
    >>> Q.toarray()
    array([[0.70710678, 0.        ],
           [0.70710678, 0.        ],
           [0.        , 0.        ],
           [0.        , 1.        ]])
    >>> R
    array([[1.41421356],
           [1.        ]])

    """
    if not isspmatrix_csr(AggOp):
        raise TypeError('expected csr_matrix for argument AggOp')

    B = np.asarray(B)
    if B.dtype not in ['float32', 'float64', 'complex64', 'complex128']:
        B = np.asarray(B, dtype='float64')

    if len(B.shape) != 2:
        raise ValueError('expected 2d array for argument B')

    if B.shape[0] % AggOp.shape[0] != 0:
        raise ValueError(f'Dimensions of AggOp {AggOp.shape} and B {B.shape} '
                         'are incompatible')

    N_fine, N_coarse = AggOp.shape

    K1 = int(B.shape[0] / N_fine)  # dof per supernode (e.g. 3 for 3d vectors)
    K2 = B.shape[1]                # candidates

    # the first two dimensions of R and Qx are collapsed later
    R = np.empty((N_coarse, K2, K2), dtype=B.dtype)    # coarse candidates
    Qx = np.empty((AggOp.nnz, K1, K2), dtype=B.dtype)  # BSR data array

    AggOp_csc = AggOp.tocsc()

    fn = amg_core.fit_candidates
    fn(N_fine, N_coarse, K1, K2,
       AggOp_csc.indptr, AggOp_csc.indices, Qx.ravel(),
       B.ravel(), R.ravel(), tol)

    Q = bsr_matrix((Qx.swapaxes(1, 2).copy(), AggOp_csc.indices,
                    AggOp_csc.indptr), shape=(K2*N_coarse, K1*N_fine))
    Q = Q.T.tobsr()
    R = R.reshape(-1, K2)

    return Q, R


# -*- coding: utf-8 -*-
"""
Created on Thu Jan  7 11:16:08 2021

@author: Annika
"""

import numpy as np
import string
from datetime import datetime

# To get same time values as in Matlab
def datenum(d):
    return 366 + d.toordinal() + (d - datetime.fromordinal(d.toordinal())).total_seconds()/(24*60*60)

day = '2019-02-22'
def read_time_rides(time_rides):
    lines = [line.rstrip('\n') for line in open(time_rides)]
    start = lines[0:len(lines):3]
    end = lines[1:len(lines):3]
    start_time = np.zeros(len(start))
    end_time = np.zeros(len(start))
    ## Split time start and end vectors
    for x in range(0,len(start)):
        start[x] = start[x].translate({ord(c): None for c in string.whitespace})
        input_string = day+' '+start[x]
        time_string = datetime.strptime(input_string,'%Y-%m-%d %H:%M:%S')
        start_time[x] = datenum(time_string)
        end[x] = end[x].translate({ord(c): None for c in string.whitespace})
        input_string = day+' '+end[x]
        time_string = datetime.strptime(input_string,'%Y-%m-%d %H:%M:%S')
        end_time[x] = datenum(time_string)
    return start_time,end_time


import torchvision.transforms as T
try:
    from torchvision.transforms import GaussianBlur
except ImportError:
    from .gaussian_blur import GaussianBlur
    T.GaussianBlur = GaussianBlur

class MoCoTransform():
    def __init__(self, image_size, mean_std, s=1.0):
        image_size = 224 if image_size is None else image_size
        self.single = False
        self.transform = T.Compose([
            # T.RandomResizedCrop(image_size), # For CIFAR10 and MNIST
            T.RandomResizedCrop(image_size, scale=(0.2, 1.0)),
            T.RandomHorizontalFlip(p=0.5),
            T.RandomApply([T.ColorJitter(0.4, 0.4, 0.4, 0.1)], p=0.8),
            T.RandomGrayscale(p=0.2),
            T.ToTensor(),
            T.Normalize(*mean_std)
        ])
    def __call__(self, x):
        x1 = self.transform(x)
        x2 = self.transform(x)
        return x1, x2


class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []
        leng = len(nums)
        if len(nums) == 0 or nums == None:
            return res
        list = []
        list.append(nums[0])
        res.append(list)
        i = 1
        while i < leng:
            size = len(res)
            for j in range(size):
                size2 = len(res[0])
                for k in range(size2 + 1):
                    tmp = res[0].copy()
                    tmp.insert(k, nums[i])
                    res.append(tmp)
                del (res[0])
            i += 1
        return res


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 17-9-18 下午3:59
# @Author  : MaybeShewill-CV
# @Site    : https://github.com/MaybeShewill-CV/CRNN_Tensorflow
# @File    : cnn_basenet.py
# @IDE: PyCharm Community Edition
"""
The base convolution neural networks mainly implement some useful cnn functions
"""
import tensorflow as tf
from tensorflow.python.training import moving_averages
from tensorflow.contrib.framework import add_model_variable
import numpy as np

class CNNBaseModel(object):
    """
    Base model for other specific cnn ctpn_models
    """

    def __init__(self):
        pass

    @staticmethod
    def conv2d(inputdata, out_channel, kernel_size, padding='SAME',
               stride=1, w_init=None, b_init=None,
               split=1, use_bias=True, data_format='NHWC', name=None):
        """
        Packing the tensorflow conv2d function.
        :param name: op name
        :param inputdata: A 4D tensorflow tensor which ust have known number of channels, but can have other
        unknown dimensions.
        :param out_channel: number of output channel.
        :param kernel_size: int so only support square kernel convolution
        :param padding: 'VALID' or 'SAME'
        :param stride: int so only support square stride
        :param w_init: initializer for convolution weights
        :param b_init: initializer for bias
        :param split: split channels as used in Alexnet mainly group for GPU memory save.
        :param use_bias:  whether to use bias.
        :param data_format: default set to NHWC according tensorflow
        :return: tf.Tensor named ``output``
        """
        with tf.variable_scope(name):
            in_shape = inputdata.get_shape().as_list()
            channel_axis = 3 if data_format == 'NHWC' else 1
            in_channel = in_shape[channel_axis]

            assert in_channel is not None, "[Conv2D] Input cannot have unknown channel!"
            assert in_channel % split == 0
            assert out_channel % split == 0

            padding = padding.upper()

            if isinstance(kernel_size, list):
                filter_shape = [kernel_size[0], kernel_size[1]] + [in_channel / split, out_channel]
            else:
                filter_shape = [kernel_size, kernel_size] + [in_channel / split, out_channel]

            if isinstance(stride, list):
                strides = [1, stride[0], stride[1], 1] if data_format == 'NHWC' \
                    else [1, 1, stride[0], stride[1]]
            else:
                strides = [1, stride, stride, 1] if data_format == 'NHWC' \
                    else [1, 1, stride, stride]

            if w_init is None:
                w_init = tf.contrib.layers.variance_scaling_initializer()
            if b_init is None:
                b_init = tf.constant_initializer()

            w = tf.get_variable('W', filter_shape, initializer=w_init)
            b = None

            if use_bias:
                b = tf.get_variable('b', [out_channel], initializer=b_init)

            if split == 1:
                conv = tf.nn.conv2d(inputdata, w, strides, padding, data_format=data_format)
            else:
                inputs = tf.split(inputdata, split, channel_axis)
                kernels = tf.split(w, split, 3)
                outputs = [tf.nn.conv2d(i, k, strides, padding, data_format=data_format)
                           for i, k in zip(inputs, kernels)]
                conv = tf.concat(outputs, channel_axis)

            ret = tf.identity(tf.nn.bias_add(conv, b, data_format=data_format)
                              if use_bias else conv, name=name)

        return ret

    @staticmethod
    def relu(inputdata, name=None):
        """

        :param name:
        :param inputdata:
        :return:
        """
        return tf.nn.relu(features=inputdata, name=name)

    @staticmethod
    def sigmoid(inputdata, name=None):
        """

        :param name:
        :param inputdata:
        :return:
        """
        return tf.nn.sigmoid(x=inputdata, name=name)

    @staticmethod
    def maxpooling(inputdata, kernel_size, stride=None, padding='VALID',
                   data_format='NHWC', name=None):
        """

        :param name:
        :param inputdata:
        :param kernel_size:
        :param stride:
        :param padding:
        :param data_format:
        :return:
        """
        padding = padding.upper()

        if stride is None:
            stride = kernel_size

        if isinstance(kernel_size, list):
            kernel = [1, kernel_size[0], kernel_size[1], 1] if data_format == 'NHWC' else \
                [1, 1, kernel_size[0], kernel_size[1]]
        else:
            kernel = [1, kernel_size, kernel_size, 1] if data_format == 'NHWC' \
                else [1, 1, kernel_size, kernel_size]

        if isinstance(stride, list):
            strides = [1, stride[0], stride[1], 1] if data_format == 'NHWC' \
                else [1, 1, stride[0], stride[1]]
        else:
            strides = [1, stride, stride, 1] if data_format == 'NHWC' \
                else [1, 1, stride, stride]

        return tf.nn.max_pool(value=inputdata, ksize=kernel, strides=strides, padding=padding,
                              data_format=data_format, name=name)

    @staticmethod
    def avgpooling(inputdata, kernel_size, stride=None, padding='VALID',
                   data_format='NHWC', name=None):
        """

        :param name:
        :param inputdata:
        :param kernel_size:
        :param stride:
        :param padding:
        :param data_format:
        :return:
        """
        if stride is None:
            stride = kernel_size

        kernel = [1, kernel_size, kernel_size, 1] if data_format == 'NHWC' \
            else [1, 1, kernel_size, kernel_size]

        strides = [1, stride, stride, 1] if data_format == 'NHWC' else [1, 1, stride, stride]

        return tf.nn.avg_pool(value=inputdata, ksize=kernel, strides=strides, padding=padding,
                              data_format=data_format, name=name)

    @staticmethod
    def globalavgpooling(inputdata, data_format='NHWC', name=None):
        """

        :param name:
        :param inputdata:
        :param data_format:
        :return:
        """
        assert inputdata.shape.ndims == 4
        assert data_format in ['NHWC', 'NCHW']

        axis = [1, 2] if data_format == 'NHWC' else [2, 3]

        return tf.reduce_mean(input_tensor=inputdata, axis=axis, name=name)

    @staticmethod
    def layernorm(inputdata, epsilon=1e-5, use_bias=True, use_scale=True,
                  data_format='NHWC', name=None):
        """
        :param name:
        :param inputdata:
        :param epsilon: epsilon to avoid divide-by-zero.
        :param use_bias: whether to use the extra affine transformation or not.
        :param use_scale: whether to use the extra affine transformation or not.
        :param data_format:
        :return:
        """
        shape = inputdata.get_shape().as_list()
        ndims = len(shape)
        assert ndims in [2, 4]

        mean, var = tf.nn.moments(inputdata, list(range(1, len(shape))), keep_dims=True)

        if data_format == 'NCHW':
            channnel = shape[1]
            new_shape = [1, channnel, 1, 1]
        else:
            channnel = shape[-1]
            new_shape = [1, 1, 1, channnel]
        if ndims == 2:
            new_shape = [1, channnel]

        if use_bias:
            beta = tf.get_variable('beta', [channnel], initializer=tf.constant_initializer())
            beta = tf.reshape(beta, new_shape)
        else:
            beta = tf.zeros([1] * ndims, name='beta')
        if use_scale:
            gamma = tf.get_variable('gamma', [channnel], initializer=tf.constant_initializer(1.0))
            gamma = tf.reshape(gamma, new_shape)
        else:
            gamma = tf.ones([1] * ndims, name='gamma')

        return tf.nn.batch_normalization(inputdata, mean, var, beta, gamma, epsilon, name=name)

    @staticmethod
    def instancenorm(inputdata, epsilon=1e-5, data_format='NHWC', use_affine=True, name=None):
        """

        :param name:
        :param inputdata:
        :param epsilon:
        :param data_format:
        :param use_affine:
        :return:
        """
        shape = inputdata.get_shape().as_list()
        if len(shape) != 4:
            raise ValueError("Input data of instancebn layer has to be 4D tensor")

        if data_format == 'NHWC':
            axis = [1, 2]
            ch = shape[3]
            new_shape = [1, 1, 1, ch]
        else:
            axis = [2, 3]
            ch = shape[1]
            new_shape = [1, ch, 1, 1]
        if ch is None:
            raise ValueError("Input of instancebn require known channel!")

        mean, var = tf.nn.moments(inputdata, axis, keep_dims=True)

        if not use_affine:
            return tf.divide(inputdata - mean, tf.sqrt(var + epsilon), name='output')

        beta = tf.get_variable('beta', [ch], initializer=tf.constant_initializer())
        beta = tf.reshape(beta, new_shape)
        gamma = tf.get_variable('gamma', [ch], initializer=tf.constant_initializer(1.0))
        gamma = tf.reshape(gamma, new_shape)
        return tf.nn.batch_normalization(inputdata, mean, var, beta, gamma, epsilon, name=name)

    @staticmethod
    def dropout(inputdata, keep_prob, is_training, name, noise_shape=None):
        """

        :param name:
        :param inputdata:
        :param keep_prob:
        :param is_training
        :param noise_shape:
        :return:
        """

        return tf.cond(
            pred=is_training,
            true_fn=lambda: tf.nn.dropout(
                inputdata, keep_prob=keep_prob, noise_shape=noise_shape
            ),
            false_fn=lambda: inputdata,
            name=name
        )

    @staticmethod
    def fullyconnect(inputdata, out_dim, w_init=None, b_init=None,
                     use_bias=True, name=None):
        """
        Fully-Connected layer, takes a N>1D tensor and returns a 2D tensor.
        It is an equivalent of `tf.layers.dense` except for naming conventions.

        :param inputdata:  a tensor to be flattened except for the first dimension.
        :param out_dim: output dimension
        :param w_init: initializer for w. Defaults to `variance_scaling_initializer`.
        :param b_init: initializer for b. Defaults to zero
        :param use_bias: whether to use bias.
        :param name:
        :return: tf.Tensor: a NC tensor named ``output`` with attribute `variables`.
        """
        shape = inputdata.get_shape().as_list()[1:]
        if None not in shape:
            inputdata = tf.reshape(inputdata, [-1, int(np.prod(shape))])
        else:
            inputdata = tf.reshape(inputdata, tf.stack([tf.shape(inputdata)[0], -1]))

        if w_init is None:
            w_init = tf.contrib.layers.variance_scaling_initializer()
        if b_init is None:
            b_init = tf.constant_initializer()

        ret = tf.layers.dense(inputs=inputdata, activation=lambda x: tf.identity(x, name='output'),
                              use_bias=use_bias, name=name,
                              kernel_initializer=w_init,
                              bias_initializer=b_init,
                              trainable=True, units=out_dim)
        return ret

    @staticmethod
    def layerbn(inputdata, is_training, name, momentum=0.999, eps=1e-3):
        """

        :param inputdata:
        :param is_training:
        :param name:
        :param momentum:
        :param eps:
        :return:
        """

        return tf.layers.batch_normalization(
            inputs=inputdata, training=is_training, name=name, momentum=momentum, epsilon=eps)

    @staticmethod
    def layerbn_distributed(list_input, stats_mode, data_format='NHWC',
                            float_type=tf.float32, trainable=True,
                            use_gamma=True, use_beta=True, bn_epsilon=1e-5,
                            bn_ema=0.9, name='BatchNorm'):
        """
        Batch norm for distributed training process
        :param list_input:
        :param stats_mode:
        :param data_format:
        :param float_type:
        :param trainable:
        :param use_gamma:
        :param use_beta:
        :param bn_epsilon:
        :param bn_ema:
        :param name:
        :return:
        """

        def _get_bn_variables(_n_out, _use_scale, _use_bias, _trainable, _float_type):

            if _use_bias:
                _beta = tf.get_variable('beta', [_n_out],
                                        initializer=tf.constant_initializer(),
                                        trainable=_trainable,
                                        dtype=_float_type)
            else:
                _beta = tf.zeros([_n_out], name='beta')
            if _use_scale:
                _gamma = tf.get_variable('gamma', [_n_out],
                                         initializer=tf.constant_initializer(1.0),
                                         trainable=_trainable,
                                         dtype=_float_type)
            else:
                _gamma = tf.ones([_n_out], name='gamma')

            _moving_mean = tf.get_variable('moving_mean', [_n_out],
                                           initializer=tf.constant_initializer(),
                                           trainable=False,
                                           dtype=_float_type)
            _moving_var = tf.get_variable('moving_variance', [_n_out],
                                          initializer=tf.constant_initializer(1),
                                          trainable=False,
                                          dtype=_float_type)
            return _beta, _gamma, _moving_mean, _moving_var

        def _update_bn_ema(_xn, _batch_mean, _batch_var, _moving_mean, _moving_var, _decay):

            _update_op1 = moving_averages.assign_moving_average(
                _moving_mean, _batch_mean, _decay, zero_debias=False,
                name='mean_ema_op')
            _update_op2 = moving_averages.assign_moving_average(
                _moving_var, _batch_var, _decay, zero_debias=False,
                name='var_ema_op')
            add_model_variable(moving_mean)
            add_model_variable(moving_var)

            # seems faster than delayed update, but might behave otherwise in distributed settings.
            with tf.control_dependencies([_update_op1, _update_op2]):
                return tf.identity(xn, name='output')

        # ======================== Checking valid values =========================
        if data_format not in ['NHWC', 'NCHW']:
            raise TypeError(
                "Only two data formats are supported at this moment: 'NHWC' or 'NCHW', "
                "%s is an unknown data format." % data_format)
        assert type(list_input) == list

        # ======================== Setting default values =========================
        shape = list_input[0].get_shape().as_list()
        assert len(shape) in [2, 4]
        n_out = shape[-1]
        if data_format == 'NCHW':
            n_out = shape[1]

        # ======================== Main operations =============================
        means = []
        square_means = []
        for i in range(len(list_input)):
            with tf.device('/gpu:%d' % i):
                batch_mean = tf.reduce_mean(list_input[i], [0, 1, 2])
                batch_square_mean = tf.reduce_mean(tf.square(list_input[i]), [0, 1, 2])
                means.append(batch_mean)
                square_means.append(batch_square_mean)

        # if your GPUs have NVLinks and you've install NCCL2, you can change `/cpu:0` to `/gpu:0`
        with tf.device('/cpu:0'):
            shape = tf.shape(list_input[0])
            num = shape[0] * shape[1] * shape[2] * len(list_input)
            mean = tf.reduce_mean(means, axis=0)
            var = tf.reduce_mean(square_means, axis=0) - tf.square(mean)
            var *= tf.cast(num, float_type) / tf.cast(num - 1, float_type)  # unbiased variance

        list_output = []
        for i in range(len(list_input)):
            with tf.device('/gpu:%d' % i):
                with tf.variable_scope(name, reuse=i > 0):
                    beta, gamma, moving_mean, moving_var = _get_bn_variables(
                        n_out, use_gamma, use_beta, trainable, float_type)

                    if 'train' in stats_mode:
                        xn = tf.nn.batch_normalization(
                            list_input[i], mean, var, beta, gamma, bn_epsilon)
                        if tf.get_variable_scope().reuse or 'gather' not in stats_mode:
                            list_output.append(xn)
                        else:
                            # gather stats and it is the main gpu device.
                            xn = _update_bn_ema(xn, mean, var, moving_mean, moving_var, bn_ema)
                            list_output.append(xn)
                    else:
                        xn = tf.nn.batch_normalization(
                            list_input[i], moving_mean, moving_var, beta, gamma, bn_epsilon)
                        list_output.append(xn)

        return list_output

    @staticmethod
    def layergn(inputdata, name, group_size=32, esp=1e-5):
        """

        :param inputdata:
        :param name:
        :param group_size:
        :param esp:
        :return:
        """
        with tf.variable_scope(name):
            inputdata = tf.transpose(inputdata, [0, 3, 1, 2])
            n, c, h, w = inputdata.get_shape().as_list()
            group_size = min(group_size, c)
            inputdata = tf.reshape(inputdata, [-1, group_size, c // group_size, h, w])
            mean, var = tf.nn.moments(inputdata, [2, 3, 4], keep_dims=True)
            inputdata = (inputdata - mean) / tf.sqrt(var + esp)

            # 每个通道的gamma和beta
            gamma = tf.Variable(tf.constant(1.0, shape=[c]), dtype=tf.float32, name='gamma')
            beta = tf.Variable(tf.constant(0.0, shape=[c]), dtype=tf.float32, name='beta')
            gamma = tf.reshape(gamma, [1, c, 1, 1])
            beta = tf.reshape(beta, [1, c, 1, 1])

            # 根据论文进行转换 [n, c, h, w, c] 到 [n, h, w, c]
            output = tf.reshape(inputdata, [-1, c, h, w])
            output = output * gamma + beta
            output = tf.transpose(output, [0, 2, 3, 1])

        return output

    @staticmethod
    def squeeze(inputdata, axis=None, name=None):
        """

        :param inputdata:
        :param axis:
        :param name:
        :return:
        """
        return tf.squeeze(input=inputdata, axis=axis, name=name)

    @staticmethod
    def deconv2d(inputdata, out_channel, kernel_size, padding='SAME',
                 stride=1, w_init=None, b_init=None,
                 use_bias=True, activation=None, data_format='channels_last',
                 trainable=True, name=None):
        """
        Packing the tensorflow conv2d function.
        :param name: op name
        :param inputdata: A 4D tensorflow tensor which ust have known number of channels, but can have other
        unknown dimensions.
        :param out_channel: number of output channel.
        :param kernel_size: int so only support square kernel convolution
        :param padding: 'VALID' or 'SAME'
        :param stride: int so only support square stride
        :param w_init: initializer for convolution weights
        :param b_init: initializer for bias
        :param activation: whether to apply a activation func to deconv result
        :param use_bias:  whether to use bias.
        :param data_format: default set to NHWC according tensorflow
        :param trainable:
        :return: tf.Tensor named ``output``
        """
        with tf.variable_scope(name):
            in_shape = inputdata.get_shape().as_list()
            channel_axis = 3 if data_format == 'channels_last' else 1
            in_channel = in_shape[channel_axis]
            assert in_channel is not None, "[Deconv2D] Input cannot have unknown channel!"

            padding = padding.upper()

            if w_init is None:
                w_init = tf.contrib.layers.variance_scaling_initializer()
            if b_init is None:
                b_init = tf.constant_initializer()

            ret = tf.layers.conv2d_transpose(inputs=inputdata, filters=out_channel,
                                             kernel_size=kernel_size,
                                             strides=stride, padding=padding,
                                             data_format=data_format,
                                             activation=activation, use_bias=use_bias,
                                             kernel_initializer=w_init,
                                             bias_initializer=b_init, trainable=trainable,
                                             name=name)
        return ret

    @staticmethod
    def dilation_conv(input_tensor, k_size, out_dims, rate, padding='SAME',
                      w_init=None, b_init=None, use_bias=False, name=None):
        """

        :param input_tensor:
        :param k_size:
        :param out_dims:
        :param rate:
        :param padding:
        :param w_init:
        :param b_init:
        :param use_bias:
        :param name:
        :return:
        """
        with tf.variable_scope(name):
            in_shape = input_tensor.get_shape().as_list()
            in_channel = in_shape[3]

            assert in_channel is not None, "[Conv2D] Input cannot have unknown channel!"

            padding = padding.upper()

            if isinstance(k_size, list):
                filter_shape = [k_size[0], k_size[1]] + [in_channel, out_dims]
            else:
                filter_shape = [k_size, k_size] + [in_channel, out_dims]

            if w_init is None:
                w_init = tf.contrib.layers.variance_scaling_initializer()
            if b_init is None:
                b_init = tf.constant_initializer()

            w = tf.get_variable('W', filter_shape, initializer=w_init)
            b = None

            if use_bias:
                b = tf.get_variable('b', [out_dims], initializer=b_init)

            conv = tf.nn.atrous_conv2d(value=input_tensor, filters=w, rate=rate,
                                       padding=padding, name='dilation_conv')

            if use_bias:
                ret = tf.add(conv, b)
            else:
                ret = conv

        return ret

    @staticmethod
    def spatial_dropout(input_tensor, keep_prob, is_training, name, seed=1234):
        """
        空间dropout实现
        :param input_tensor:
        :param keep_prob:
        :param is_training:
        :param name:
        :param seed:
        :return:
        """

        def f1():
            input_shape = input_tensor.get_shape().as_list()
            noise_shape = tf.constant(value=[input_shape[0], 1, 1, input_shape[3]])
            return tf.nn.dropout(input_tensor, keep_prob, noise_shape, seed=seed, name="spatial_dropout")

        def f2():
            return input_tensor

        with tf.variable_scope(name_or_scope=name):

            output = tf.cond(is_training, f1, f2)

            return output

    @staticmethod
    def lrelu(inputdata, name, alpha=0.2):
        """

        :param inputdata:
        :param alpha:
        :param name:
        :return:
        """
        with tf.variable_scope(name):
            return tf.nn.relu(inputdata) - alpha * tf.nn.relu(-inputdata)

    @staticmethod
    def pad(inputdata, paddings, name):
        """

        :param inputdata:
        :param paddings:
        :return:
        """
        with tf.variable_scope(name_or_scope=name):
            return tf.pad(tensor=inputdata, paddings=paddings)


# """
# This is BinaryMatrix's API interface.
# You should not implement it, or speculate about its implementation
# """
# class BinaryMatrix(object):
#     def get(self, x: int, y: int) -> int:
#     def dimensions(self) -> list[]:

class Solution:
    def leftMostColumnWithOne(self, bm):
        r, c = bm.dimensions()
        x = c-1
        y = 0
        # debug
        while(x >= 0 and y < r):
            val = bm.get(y, x)
            if val == 1:
                x -= 1
            else:
                y += 1

        if (y == r and x >= 0) or (x < 0 and y < r):
            x += 1
        if x == c:
            return -1
        return x

LeetCode/30 Day LeetCoding Challenge/Week 3/21_leftmost_column_with_at_least_a_one.py


from docx import Document
from docx.shared import Pt,RGBColor
from docx.oxml.ns import qn
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT  #对其方式

doc = Document("demp.docx")

for paragraph in doc.paragraphs:
    paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    paragraph.paragraph_format.line_spacing = 2.0

    for run in paragraph.runs:
        run.font.bold = True
        run.font.italic = True
        run.font.underline = True
        run.font.strike = True
        run.font.shadow = True
        run.font.size = Pt(20)
        run.font.color.rgb = RGBColor(255,255,255)
        run.font.name = '微软雅黑'
        r = run._element.rPr.rFonts
        r.set(qn('w:easrAsia'),'微软雅黑')

doc.save('demp1.docx')


from measure import Measurer
from multiprocessing import cpu_count
from datetime import datetime
from openpyxl import load_workbook
import pandas as pd
import os

def info(msg):
    print('[INFO]: {}'.format(msg))

def make_report(name, counter, builder, phi_np, phi_gd):
    name = name.replace('\n', ' ').strip()
    print('=' * 64)
    info('Функция {} #{} (Потоков: {}, размерность: {}, '
         'точек: {}, точность: {})'.format(name, counter, cpu_count(),
                                           builder.dim + 1, len(phi_np),
                                           builder.acc))
    full_time = round(Measurer.time_measures[0].total_seconds(), 2)
    gd_time = round(Measurer.time_measures[1].total_seconds(), 2)
    info('Время полного перебора: {} секунд'.format(full_time))
    info('Время градиентного спуска: {} секунд'.format(gd_time))
    max_error = round(max([abs(phi_gd[j] - phi_np[j])
                           for j in range(len(phi_gd))]), 5)
    avg_error = round(sum([abs(phi_gd[j] - phi_np[j])
                           for j in range(len(phi_gd))])
                      / len(phi_gd), 5)
    info('Максимальная ошибка: {}'.format(max_error))
    info('Средняя ошибка: {}'.format(avg_error))
    counter += 1
    Measurer.time_measures.clear()
    print('=' * 64)
    return ['\n{}\n'.format(name), builder.dim + 1, cpu_count(), len(phi_np),
            builder.acc,
            full_time, gd_time, max_error, avg_error]

def get_available_name():
    sheets = 1
    now = datetime.now()
    name = now.strftime('%Y-%m-%d')
    path = os.path.join(os.getcwd(), 'Report', '{}.xlsx'.format(name))
    if os.path.exists(path):
        xl = pd.ExcelFile(path)
        sheets = len(xl.sheet_names) + 1

    return path, 'Report{}'.format(sheets)

def write_statistics(reports):
    df = pd.DataFrame(reports, columns=['Function Name', 'Dimensions',
                                        'Threads', 'Dots amount',
                                        'Accuracy (% of dots used)', 'Alpha',
                                        'Full NP time (sec)', 'GD time (sec)',
                                        'Max error', 'Average Error'])
    path, sheet = get_available_name()
    with pd.ExcelWriter(path, engine='openpyxl') as writer:
        if os.path.exists(path):
            writer.book = load_workbook(path)
        df.to_excel(writer, index=True, sheet_name=sheet)
        worksheet = writer.sheets[sheet]
        worksheet.sheet_view.zoomScale = 50
        worksheet.column_dimensions['A'].width = 4
        worksheet.column_dimensions['B'].width = 32
        for d in ['C', 'D', 'E', 'F']:
            worksheet.column_dimensions[d].width = 16
        for d in ['G', 'H', 'I', 'J', 'K']:
            worksheet.column_dimensions[d].width = 24
        writer.save()

def get_neighbours(index, dim, ann):
    moves = [ann ** d for d in range(dim)]
    for i in range(dim):
        if index + moves[i] < ann ** dim:
            yield index + moves[i]
        if index - moves[i] >= 0:
            yield index - moves[i]


import torch
from pycocotools.coco import COCO
import fasttext
from cocoTripletDataset import TripletCOCO_Img2Text, TripletCOCO_Text2Img
import os
import torch.nn as nn
from matplotlib import pyplot as plt
from torchvision.models import resnet18, ResNet18_Weights
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights, fasterrcnn_resnet50_fpn_v2
from torch.utils.data import DataLoader
from net import FasterRCNN_Triplet_COCO
from torchvision import transforms
import re
import numpy as np
import wandb

train_jsonPath = r"/ghome/group04/mcv/datasets/COCO/instances_train2014.json"
train_jsonPathCap= r"/ghome/group04/mcv/datasets/COCO/captions_train2014.json"
train_trainImagesPath = r"/ghome/group04/mcv/datasets/COCO/train2014"

val_jsonPath = r"/ghome/group04/mcv/datasets/COCO/instances_val2014.json"
val_jsonPathCap= r"/ghome/group04/mcv/datasets/COCO/captions_val2014.json"
val_trainImagesPath = r"/ghome/group04/mcv/datasets/COCO/val2014"

fasttext_model_path = r"/home/mcv/m5/fasttext_wiki.en.bin"

out_text_model_path = r"best_text_taskb.pth"
out_img_model_path = r"best_img_taskb.pth"

device = "cuda"
batch_size = 32
n_epochs = 4
log_interval = 5
size = (240,320)

text_pre_model = fasttext.load_model(path=fasttext_model_path)
img_pre_model = FasterRCNN_Triplet_COCO().to(device)

# Transform
transformsPretrained = FasterRCNN_ResNet50_FPN_Weights.COCO_V1.transforms()
transformsAp = torch.nn.Sequential(
    transformsPretrained,
    transforms.Resize(size),
)

img_model = nn.Sequential(
    nn.Linear(1024, 256),
    nn.Dropout(),
    nn.ReLU(),
    nn.Linear(256, 128)
).to(device).eval()

text_model = nn.Sequential(
    nn.Linear(300, 256),
    nn.Dropout(),
    nn.ReLU(),
    nn.Linear(256, 128)
).to(device).eval()

train_dataset = TripletCOCO_Text2Img(train_trainImagesPath, os.listdir(train_trainImagesPath), train_jsonPath, train_jsonPathCap, transformsAp)
val_dataset = TripletCOCO_Text2Img(val_trainImagesPath, os.listdir(val_trainImagesPath), val_jsonPath, val_jsonPathCap, transformsAp)

def sentence2vector(sentence, model):
    sentence = re.sub(r'[^a-zA-Z]', '', sentence).lower()
    words = sentence.split()
    vectors = [model.get_word_vector(word) for word in words]
    summary_vector = np.mean(vectors, axis=0)

    return summary_vector

criterion = nn.TripletMarginLoss()
optimizer = torch.optim.Adam(
    list(img_model.parameters()) + list(text_model.parameters()),
    lr=3e-4
)

def prep_sentence(sentence, text_pre_model):
    sentence = text_pre_model.get_sentence_vector(
        re.sub(r'[^a-zA-Z ]', '', sentence.lower())
    )
    return sentence

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)

wandb.init(project="text2img")
min_val_loss = 100
for epoch in range(n_epochs):
    img_model.train()
    text_model.train()

    for img, pos, neg in train_loader:

        img = img.to(device)
        pos = [prep_sentence(p, text_pre_model) for p in pos]
        neg = neg.to(device)
        pos = torch.tensor(pos, device=device)
        with torch.no_grad():
            img_feature_vector = img_pre_model(img)
            neg_feature_vector = img_pre_model(neg)

        img_common = img_model(img_feature_vector.reshape((-1, img_feature_vector.shape[1])))
        neg_img = img_model(neg_feature_vector.reshape((-1, neg_feature_vector.shape[1])))
        pos_common  = text_model(pos)

        loss = criterion(pos_common,img_common, neg_img)
        img_model.zero_grad()
        text_model.zero_grad()
        loss.backward()
        optimizer.step()

        #if epoch % log_interval == 0:
        print(f"Train loss: {loss.item()}")
        wandb.log({"train_loss": loss})

    img_model.eval()
    text_model.eval()
    losses = []
    for img, pos, neg in val_loader:
        img = img.to(device)
        pos = [prep_sentence(p, text_pre_model) for p in pos]
        neg = neg.to(device)
        pos = torch.tensor(pos, device=device)
        with torch.no_grad():
            img_feature_vector = img_pre_model(img)
            neg_feature_vector = img_pre_model(neg)

        img_common = img_model(img_feature_vector.reshape((-1, img_feature_vector.shape[1])))
        neg_img = img_model(neg_feature_vector.reshape((-1, neg_feature_vector.shape[1])))
        pos_common  = text_model(pos)

        loss = criterion(pos_common,img_common, neg_img)
        losses.append(loss.item())
    val_loss = torch.mean(torch.tensor(losses))
    if val_loss < min_val_loss:
        min_val_loss = val_loss
        torch.save(img_model.state_dict(), out_img_model_path)
        torch.save(text_model.state_dict(), out_text_model_path)
    print(f"Val loss: {val_loss}")
    wandb.log({"val_loss": val_loss})


import os,json, openpyxl
from openpyxl import Workbook, load_workbook
from openpyxl.drawing.image import Image

# DATA_FILE = "Műszaknapló_H4_2021.xlsx"
DATA_FILE = input("Kérem az excel file nevét amiből dolgozzak:")
WORK_FILE = input("Kérem az excel file nevét amibe dolgozzak:")
nap = int(input("Hanyadikára?:"))
muszak = int(input("Napszak?(1, 2, vagy 3):"))

if DATA_FILE == "":
    DATA_FILE = "Műszaknapló_H4_2021.xlsx"

if WORK_FILE == "":
    WORK_FILE = "tuloracsoportos.xlsx"

tol = 6
ig = 14
ora = 8
elrend_cod = 2
elrend_cod2 = 1

if muszak == 1:
    tol = 6
    ig = 14
elif muszak == 2:
    tol = 14
    ig = 22
elif muszak == 3:
    tol = 22
    ig = 6

wb = Workbook()
wbl = load_workbook(filename = DATA_FILE)
ws = wb.active
tulora = load_workbook(filename = WORK_FILE)
lapp = tulora.active

lapp["C3"] = "2021. Február"

oszlop = nap + 4
a = 3
b = 10
c = -1

sorszam = 0
for row in range(a, 59):
    a = a+1
    sheet_ranges = wbl["2021 Február"]
    if not (sheet_ranges[f"C{a}"].value):continue

    elif sheet_ranges.cell(row=a, column=oszlop).value != muszak : continue
    # elif (sheet_ranges[f"Q{a}"].value != muszak): continue
    b = b+1
    c = c+1
    # print(b, a)
    print(sheet_ranges[f"B{a}"].value, sheet_ranges[f"C{a}"].value, nap)

    tsz = ws[f"B{b}"] = sheet_ranges[f"B{a}"].value
    name = ws[f"C{b}"] = sheet_ranges[f"C{a}"].value

    uj_adat = [tsz, name, nap, tol, ig, 8, 2, "", "", "", "", 1]

    if c == 25:
        Kep = "matra.jpg"
        my_png = openpyxl.drawing.image.Image(Kep)
        lapp.add_image(Kep, 'A1')

        tulora.save(f"tulora_aktualis{sorszam}.xlsx")

        sorszam = sorszam+1
        c = 0
        b = 11

    for row in lapp[f"b{b}": f"m{b}"]:
        for index, cell in enumerate(row):
            cell.value = uj_adat[index]
            print(c, b, )

for row in range(b, 35):
    b = b+1

    tsz = ws[f"B{b}"] = ""
    name = ws[f"C{b}"] = ""
    nap = ws[f"D{b}"] = ""
    tol = ws[f"E{b}"] = ""
    ig = ws[f"F{b}"] = ""
    ora = ws[f"G{b}"] = ""
    elrend_cod = ws[f"H{b}"] = ""
    elrend_cod2 = ws[f"M{b}"] = ""

    uj_adat = [tsz, name, nap, tol, ig, ora, elrend_cod, "", "", "", "", elrend_cod2]

    for row in lapp[f"b{b}": f"m{b}"]:
        for index, cell in enumerate(row):
            cell.value = uj_adat[index]
            print(c, b)

tulora.save(f"tulora_aktualis{sorszam+1}.xlsx")


from time import sleep
from appium import webdriver
import android.common_btn
import android.welcome
import android.create_new_account
import android.login
import android.main_screen
import android.wifi_binding
import android.android_btn
import android.dashboard
import android.schedule
import android.sys_info
import android.zone
import android.control_app
import android.gtk_event
import android.verify.click
import android.verify.compare
import android.verify.exist

def sanity_001(self):
    android.control_app.is_app_installed(self)
    android.control_app.remove_app(self)
    android.control_app.install_app(self, "1.65.3")
    android.control_app.launch_app(self)
    android.welcome.slide_welcome_frame(self)
    android.login.sign_in(self, "changdarcy", "changdarcy")

def sanity_002(self):
#    android.main_screen.my_account(self)
#    android.login.sign_out(self)
    android.welcome.slide_welcome_frame(self)
    android.login.sign_in(self, "changdarcy", "changdarcy")

def sanity_004(self):
    android.main_screen.my_account(self)
    android.main_screen.user_manual(self)
    android.verify.compare.verify_user_manual(self)
    android.android_btn.android_back(self)

def sanity_009(self):
    android.main_screen.my_account(self)
    android.verify.compare.verify_app_version(self)

def sanity_010(self):
    android.main_screen.add_device(self)
    android.wifi_binding.cloud_control(self)
    android.common_btn.slide_right2left(self)
    android.wifi_binding.open_wifi_setting(self)
#    android.wifi_binding.wifi_connect_sprinkler(self, "BUZZISPR_1FB5AC")
    android.wifi_binding.wifi_connect_sprinkler(self, "BUZZISPR_1FB840")
    android.android_btn.android_cancel_btn(self)  # For zenfone6
    android.android_btn.android_back(self)
    android.wifi_binding.next_to_set_up_network_connection(self)
    android.wifi_binding.wireless_setup(self)
#    android.wifi_binding.wifi_connect_ap(self, "BKwifi_Darcy_2.4G", "88888888")
    android.wifi_binding.wifi_connect_ap(self, "Do", "88888888")
    android.wifi_binding.next_to_continue_setting(self)
    try:
        self.driver.find_element_by_id("com.blackloud.wetti:id/rltAb")
        android.main_screen.choose_device(self)
    except:
        pass
    android.zip_code.zip_code(self)
    android.zip_code.enter_zip_code(self, 94536)
    android.zip_code.choose_zip_code(self, 94536)
    android.wifi_binding.weather_assistant(self, "ON")
    android.wifi_binding.next_btn_for_first_setting(self)
    android.zone.set_zone_all(self)
    android.wifi_binding.next_btn_for_first_setting(self)
    android.wifi_binding.schedule(self, 1)
    android.schedule.hour(self, 1)
    android.schedule.repeat_su(self)
    android.schedule.save(self)
    android.wifi_binding.next_btn_for_first_setting(self)
    android.common_btn.ok_btn_on_bottom(self)
    android.main_screen.choose_device(self)
    android.verify.next_page.verify_binging_success(self)

def sanity_016(self):
    android.main_screen.choose_device(self)
    android.dashboard.settings(self)
    android.sys_info.edit_device_name(self, "Gemtek")
    android.sys_info.back(self)
    android.verify.compare.verify_dashboard_title(self, "Gemtek")
    android.dashboard.return2main_screen(self)
    android.verify.compare.verify_device_name(self, "Gemtek")

def sanity_017(self):
    android.main_screen.choose_device(self)
    android.dashboard.settings(self)
    android.sys_info.change_zip_code(self)
    android.zip_code.enter_zip_code(self, 22201)
    android.zip_code.choose_zip_code(self, 22201)
    android.verify.compare.verify_zip_code(self, 22201)

def sanity_018(self):  # non-sanity_018
    android.main_screen.choose_device(self)
    android.dashboard.settings(self)
    android.sys_info.change_zone(self)
    android.zone.set_zone_all(self)
    android.zone.set_zone_num(self, 1)
    android.zone.set_zone_num(self, 2)
    android.zone.set_zone_num(self, 3)
    android.zone.set_zone_num(self, 4)
    android.zone.save(self)

def sanity_021(self):
    android.main_screen.choose_device(self)
    android.dashboard.settings(self)
    android.verify.compare.verify_fw_version(self, "0.00.81")

def sanity_022(self):
    android.main_screen.choose_device(self)
    android.dashboard.schedule(self, 1)
    android.dashboard.schedule_edit(self)
    android.schedule.hour(self, 4)
    android.schedule.minute(self, 0)
    android.schedule.time_system(self, "AM")
    android.schedule.repeat_all(self)
    android.schedule.save(self)

def sanity_023(self):
    android.main_screen.choose_device(self)
    android.dashboard.schedule(self, 2)
    android.dashboard.schedule_edit(self)
    android.schedule.hour(self, 6)
    android.schedule.minute(self, 0)
    android.schedule.time_system(self, "AM")
    android.schedule.repeat_all(self)
    android.schedule.save(self)
    android.verify.exist.verify_schedule_without_pause(self)

def sanity_034(self):
    android.main_screen.choose_device(self)
    android.dashboard.schedule(self, 1)
    android.dashboard.manual(self)
    android.manual_watering.launch_watering(self, 1)
    android.verify.exist.verify_watering_zone(self, 1)
    android.verify.exist.verify_watering_image(self)
    android.verify.exist.verify_countdown(self)
    android.manual_watering.stop(self)
    android.verify.exist.verify_manual_watering_page(self)

def sanity_036(self):
    android.main_screen.choose_device(self)
    android.dashboard.manual(self)
    android.manual_watering.water_multiple_zones(self)
    android.verify.exist.verify_watering_image(self)
    android.verify.exist.verify_countdown(self)
    android.manual_watering.skip(self)
    android.verify.exist.verify_watering_image(self)
    android.verify.exist.verify_countdown(self)
    android.manual_watering.stop(self)
    android.verify.exist.verify_manual_watering_page(self)

def sanity_039(self):
    android.main_screen.choose_device(self)
    android.dashboard.zone_slide_down2up(self)
    android.dashboard.zone(self, 1)
    android.zone.name_your_zone(self, "darcy")
    android.zone.ok(self)
    android.zone.save(self)
    android.verify.compare.verify_zone_name(self, 1, "darcy")