# -*- coding: utf-8 -*-

import numpy as np
import pandas as pd
import seaborn as sns
from astropy.table import Table
from astropy.io import fits
from astropy.time import Time
import matplotlib.pyplot as plt
import datetime
import configparser
import os
import glob
import logging
import time
import subprocess
import _pickle as cpickle

def make_good_frame_list(s,cuts={'teff':0.2, 'zp':None,'psf':None}):
    """Returns a list of images for a certain chip that are of quality better than a given cut.
    Arguments:
    s (obj): Stack object
    field (str): (e.g. 'X2')
    band (str): (e.g. 'g')

    Keyword arguments:
    cuts:
        zp (float): the zeropoint cut to be used for the stack (default = None)
        psf (float): the seeing cut to be used for the stack (default = None)
        t_eff (float): the teff cut to be used for the stack (default = 0.2)

    Returns:
    good_exps (DataFrame): A reduced DataFrame of good exposures for each (field,band)
    """
    ## 1
    ## Get median ZP for each exposure
    ## And calculate residual for each chip compared to that median
    logger = logging.getLogger(__name__)
    logger.handlers =[]
    ch = logging.StreamHandler()
    '''if zp_cut>0:
        logger.setLevel(logging.DEBUG)
        ch.setLevel(logging.DEBUG)
    else:'''
    logger.setLevel(logging.DEBUG)
    ch.setLevel(logging.DEBUG)
    formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    logger.info('Initiating make_good_frame_list.py')
    info = s.info_df
    import math
    field,band,cuts = s.field,s.band,s.cuts
    info = info[info['FIELD']==field]
    logger.info('These are the bands available for field {0}'.format(field))
    logger.info(info.BAND.unique())
    info = info[info['BAND']==band]
    logger.info(cuts)

    if cuts['zp']!= 'None' and cuts['zp']!= None:
        logger.warning('Gone to do ZP residuals, not sure you want this.')
        logger.info(cuts['zp'])
        info['ZP_EXPRES']=''
        for counter,exp in enumerate(info.EXPNUM.unique()):
            this_exp = info[info['EXPNUM']==exp]
            exp_idx = this_exp.index
            med_zp = this_exp['CHIP_ZERO_POINT'].median()
            info.loc[exp_idx,'ZP_EXPRES'] = this_exp['CHIP_ZERO_POINT']-med_zp

        logger.info('Here is the column of initial residuals for each exposure')
        logger.info(info['ZP_EXPRES'])
        ######################################################
        ## 2
        ## Calculate the average residual from part 1 over all exposures for that chip (field,band)
        logger.info('Getting the median zeropoint residual for all exposures for each chip')
        info['ZP_ADJ1'] = ''
        info['ZP_SIG_ADJ1'] = ''
        for counter, chip in enumerate(info.CCDNUM.unique()):

            this_chip = info[info['CCDNUM']==chip]
            chip_idx = this_chip.index
            med_zp = this_chip['ZP_EXPRES'].median()
            sig_chip_zps = this_chip['ZP_EXPRES'].std()
            ## 3
            ## Subtract that average residual from the given ZP to give an adjusted ZP
            info.loc[chip_idx,'ZP_ADJ1']=this_chip['CHIP_ZERO_POINT']-med_zp
            info.loc[chip_idx,'ZP_SIG_ADJ1']=sig_chip_zps
        logger.debug('Here is the column of adjusted zeropoints')
        logger.debug(info['ZP_ADJ1'])
        #####################################################
        ## 4
        ## Subtract the average ZP for each year off the individual zps for that year

        years =[1,2,3,4,5]
        info['ZP_RES']=''
        for year in years:
            logger.info('Subtracting the median zeropoint for {0} from all exposures that year'.format(year))
            this_year = info[info['YEAR']==year]
            year_idx = this_year.index
            year_med = this_year['ZP_ADJ1'].median()
            year_sig = this_year['ZP_ADJ1'].std()
            final_resid = this_year['ZP_ADJ1']-year_med
            info.loc[year_idx,'ZP_RES']=final_resid

        #####################################################################
        ## 5
        ## Now cut exposures (field,band,chip) based on whether they make the cut and return them
        logger.info('Getting rid of exposures whose ZP residual is below {0}'.format(zp_cut))
        exps = info.EXPNUM.unique()
        zp_cut     = float(zp_cut)
        if cuts['psf']:
            psf_cut = float(cuts['psf'])
        nbad = 15
        good_exps = []
        good_frame = pd.DataFrame()
        for exp in exps:
            this_exp = info[info['EXPNUM']==exp]
            logger.debug('Cutting in exposure {0}'.format(exp))
            resids = this_exp['ZP_RES']
            resids = resids.as_matrix()

            bad_resids = 0
            reformatted_resids = []
            for i in range(len(resids)):
                res = resids[i]
                res = float(res)
                reformatted_resids.append(res)
                if float(res)<zp_cut:
                    bad_resids +=1
            this_exp['ZP_RES']=np.array(reformatted_resids)
            logger.debug('Number of frames in exposure {0} that fail the ZP cut: {1}'.format(exp,bad_resids))
            bads =bad_resids+len(this_exp[this_exp['PSF_NEA']>psf_cut])
            logger.info("Number of frames failing the combined ZP and PSF cut: %s" %bads)
            if bads <nbad:
                #is a good frame
                logger.debug('...is a good frame!')
                good_exps.append(exp)

                good_frame = good_frame.append(this_exp)
        good_fn = os.path.join(s.list_dir,'good_exps_%s_%s_%s_%s.fits'%(field,band,zp_cut,psf_cut))
        logger.info("%s exposures were rejected!" %(len(exps)-len(good_exps)))
    ## Save results
    elif -1 < cuts['teff'] < 500:
        logger.info('Doing the cut based on T_eff > %s'%cuts['teff'])
        good_frame = pd.DataFrame()
        good_exps = []

        for counter,exp in enumerate(info.EXPNUM.unique()):

            this_exp = info[info['EXPNUM']==exp]
            exp_idx = this_exp.index
            try:
                t_eff = this_exp['T_EFF'].values[0]
            except:
                t_eff = 2
            if t_eff < -1:
                t_eff = 2
            elif not t_eff:
                t_eff = 2
            try:
                fwhm = this_exp['FWHM_ASEC'].values[0]
            except:
                fwhm = 2
            if fwhm < -1:
                fwhm = 5
            elif not fwhm:
                fwhm = 5
            if not cuts['psf']:
                psf_cut=5
            else:
                psf_cut = cuts['psf']

            if t_eff > cuts['teff'] and len(this_exp[this_exp['PSF_NEA']>psf_cut])<15 :
                this_exp['T_EFF']= t_eff
                good_frame = good_frame.append(this_exp)
                good_exps.append(exp)
        good_fn = os.path.join(s.list_dir,'good_exps_%s_%s_%s_%s.csv'%(field,band,cuts['teff'],cuts['psf']))
    txtname = good_fn[:-4]+'txt'
    np.savetxt(txtname,good_exps,fmt='%s')
    logger.info('Writing out good exposure list to {0}'.format(good_fn))

    try:
        good_frame = good_frame.drop(['ZP_RES','ZP_EXPRES','ZP_ADJ1','ZP_SIG_ADJ1'],axis=1)
    except:
        pass
    good_frame.to_csv(good_fn)
    return good_frame

def make_swarp_cmds(s,chip,logger = None,cuts={'teff':0.2, 'zp':None,'psf':None},final=True):
    """function to make swarp command to stack Nminus1_year, field chip, band"""
    MY,field,band = s.my,s.field,s.band
    cuts = s.cuts
    if not os.path.isdir(os.path.join(s.out_dir,'MY%s'%MY,field,band)):
        os.mkdir(os.path.join(s.out_dir,'MY%s'%MY,field,band))
    logger = logging.getLogger(__name__)
    logger.handlers =[]
    logger.setLevel(logging.DEBUG)
    formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    logger.info('Preparing the frames for %s, %s band, chip %s'%(field,band, chip))
    #band = band + '    '
    ## Get the list of exposures for MY, field, chip, band.
    good = s.good_frame
    good_band = good[good['BAND']==band]
    if MY == 'none':
        good_band_my =good_band
    else:

        good_band_my = good_band[good_band['SEASON']!=int(MY)]
    good_my_exps = good_band_my['EXPNUM'].unique()
    #for each good exposure, find it's file
    stack_fns = {}
    logger.info('Adding files to the %s, %s band, chip %s stack'%(field, band,chip))
    good_band_my.sort_values('CHIP_ZERO_POINT',ascending=False,inplace=True)
    n,l=0,0
    stack_fns[0]=[]
    nights = []
    for counter,exp in enumerate(good_my_exps):

        this_exp = good_band_my[good_band_my['EXPNUM']==exp]
        first = this_exp.iloc[0]
        night = str(first['NITE'])
        #chip = first['CCDNUM']

        this_exp_fn = get_dessn_obs(s,night,exp,chip,logger)
        #logger.info("Adding file from %s" %night)
        if night not in nights:
            if this_exp_fn:
                nights.append(night)
                for fn in this_exp_fn:
                    n+=1
                    stack_fns[l].append(fn)

                    if n%100.0 == 0:
                        l+=1
                        stack_fns[l]=[]
    cmd_list = {}
    for j in range(0,l+1):
        fns = np.array(stack_fns[j])
        fn_list = os.path.join(s.temp_dir,\
        'stack_fns_MY%s_%s_%s_%s_%s_%s.lst' %(MY,field,band,chip,s.cutstring,j))
        logger.info('Saving list of files to stack at {0}'.format(fn_list))
        np.savetxt(fn_list,fns,fmt='%s')

        if final == True:
            fn_out = os.path.join(s.out_dir,'MY%s'%MY,field,band)+\
            '/ccd_%s_%s_%s_%s_clipped.fits'%(chip,band,s.cutstring,j)
        else:
            fn_out = os.path.join(s.out_dir,'MY%s'%MY,field,band)+\
            '/ccd_%s_%s_%s_%s_temp_clipped.fits'%(chip,band,s.cutstring,j)

        weightlist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.wgt.lst'%(MY,s.field,s.band,chip,s.cutstring,j))
        resamplist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.resamp.lst'%(MY,s.field,s.band,chip,s.cutstring,j))
        headerlist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.head.lst'%(MY,s.field,s.band,chip,s.cutstring,j))

        weightout_name = fn_out[:-4]+'wgt.fits'
        nofiles = 0
        if not os.path.isfile(headerlist_name):
            logger.info("%s, %s band, chip %s: Going to do resampling!"%(field,band,chip))
            try:
                weightlist_name,resamplist_name,headerlist_name = resample(s,fn_list,MY,chip,cuts,j,logger)
            except TypeError:
                logger.info("No files in list: %s" %fn_list)
                nofiles = 1
        else:
            logger.info("Header list exists: %s, \n Going to make initial stack."%headerlist_name)
        if os.path.isfile(fn_out.replace('clipped','weighted')):
            cmd_list[j] = (False,False,fn_out)
        else:
            if nofiles ==0:
                cliptab_name = os.path.join(s.temp_dir,'cliptabs','%s_%s_%s_%s_%s_%s_clipped.tab'%(MY,s.field,s.band,chip,s.cutstring,j))
                clip_sigs = {
                'SN-X1':15,
                'SN-X2':15,
                'SN-C1':15,
                'SN-C2':15,
                'SN-S1':15,
                'SN-S2':15,
                'SN-E1':15,
                'SN-E2':15,
                'SN-X3':10,
                'SN-C3':10,
                }
                swarp_clip = [
                'swarp',
                '@%s'%resamplist_name,
                '-RESAMPLE','N',
                '-IMAGEOUT_NAME',fn_out,
                '-CLIP_LOGNAME',cliptab_name,
                '-CLIP_SIGMA',str(clip_sigs[s.field])
                ]
                maskweightlist_name = resamplist_name.replace('resamp','maskweight')

                swarp_weight = [
                'swarp',
                '@%s'%resamplist_name,
                '-RESAMPLE', 'N',
                '-COMBINE_TYPE', 'WEIGHTED',
                '-WEIGHT_TYPE', 'MAP_WEIGHT',
                '-WEIGHT_IMAGE', '@%s'%maskweightlist_name,
                '-IMAGEOUT_NAME', fn_out.replace('clipped.fits','weighted.fits')
                ]
                if os.path.isfile(fn_out):
                    cmd_list[j] = (False,swarp_weight,fn_out)
                else:

                    cmd_list[j]=(swarp_clip,swarp_weight,fn_out)
            else:
                cmd_list[j]=(False,False,False)

    #logger.info(cmd_list)
    return cmd_list
#############################################

def get_des_obs_year(night,logger=None):
    if not logger:
        logger = logging.getLogger(__name__)
        logger.handlers =[]
        logger.setLevel(logging.DEBUG)
        formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(formatter)
        logger.addHandler(ch)
    night = int(night)
    cp=configparser.ConfigParser()
    # read the .ini file
    cp.read('/media/data3/wiseman/des/coadding/config/snobs_params.ini')
    # Make a list of years
    years= ['Y1','Y2','Y3','Y4','Y5']
    year_night_lims = {}
    for y in years:
        year_night_lim = cp.get('year_night_lims',y)
        year_night_lims[y]=[int(lim.strip()) for lim in year_night_lim.split(',')]
    if ((night > year_night_lims['Y1'][0]) and
        (night < year_night_lims['Y1'][1])):
        year = 'Y1'
    elif ((night > year_night_lims['Y2'][0]) and
          (night < year_night_lims['Y2'][1])):
        year = 'Y2'
    elif ((night > year_night_lims['Y3'][0]) and
          (night < year_night_lims['Y3'][1])):
        year = 'Y3'
    elif ((night > year_night_lims['Y4'][0]) and
          (night < year_night_lims['Y4'][1])):
        year = 'Y4'
    elif ((night > year_night_lims['Y5'][0]) and
          (night < year_night_lims['Y5'][1])):
        year = 'Y5'
    else:
        raise ValueError
    return year
###############################################

def get_dessn_obs(s, night, expnum, chipnum,logger=None):
    '''Function to get the filename for a DES image for a
       given field, band, night, chip, and expnum.
       Uses an object of the Stack class.
       Returns path and name of the file requested.'''
    if not logger:
        logger.handlers =[]
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.DEBUG)
        formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(formatter)
        logger.addHandler(ch)
    field, band = s.field,s.band
    #------------------------------------
    chipnum = int(chipnum)
    # step 1 - get the year of the observation
    year = get_des_obs_year(night,logger)
    #------------------------------------
    # step 2 - find the directory for this night
    year_dir = s.data_dirs[year]
    glob_str = year_dir+night+'-r????'
    subdir = glob.glob(glob_str)[-1]+'/'
    # then this field
    field_glob_str = subdir+'*%s_%s*' % (field, band)
    try:
        field_subdir = glob.glob(field_glob_str)[-1]+'/'
    except IndexError:
        #that subdir doesn't have obs for our field in it
        subdir=glob.glob(glob_str)[0]+'/'
        field_glob_str = subdir+'*%s_%s*' % (field, band)
        field_subdir = glob.glob(field_glob_str)[-1]+'/'
    procs = []
    counts  = []
    if len(os.listdir(field_subdir))==0:
        return None

    for counter,ext in enumerate(os.listdir(field_subdir)):
        procs.append(int(ext[1:]))
        counts.append(counter)
    which = np.argmax(np.array(procs))
    try:
        field_subsubdir = field_subdir + os.listdir(field_subdir)[counts[which]]+'/'
    except:
        field_subsubdir = field_subdir + os.listdir(field_subdir)[-1]+'/'
    #field_subsubdir = field_subdir + os.listdir(field_subdir)[-1]+'/'
    # then the chip
    chip_subdir = '%sccd%02d' % (field_subsubdir, chipnum)
    #------------------------------------
    # step 3 - find the final fits file at the end of the dir structure
    curr_obs_dir = os.path.join(chip_subdir,'red')
    rabbit_hole=True
    while rabbit_hole:
        obs_subdir_list = os.listdir(curr_obs_dir)
        try:
            if os.path.isfile(os.path.join(curr_obs_dir,obs_subdir_list[0])):
                obs_dir = curr_obs_dir
                rabbit_hole=False
            else:
                curr_obs_dir = os.path.join(curr_obs_dir,obs_subdir_list[0])
        except:
            logger.warning('No files in %s'%curr_obs_dir)
            return None

    #------------------------------------
    # step 4 - CHECK THIS IS THE CORRECT EXPOSURE NUMBER!!!
    obs_fns = []
    if os.path.split(obs_dir)[-1] =='red':
        list_of_imgs = glob.glob(os.path.join(obs_dir,'*immasked*'))
        if len(list_of_imgs) == 0:
            list_of_imgs = glob.glob(os.path.join(obs_dir,'*'))
    else:
        list_of_imgs = glob.glob(os.path.join(obs_dir,'*'))
    for obs_fn in list_of_imgs:
        try:
            fits_expnum = fits.getheader(obs_fn)['EXPNUM']
        except:
            continue

        if obs_fn[-9:] =='sked.fits':
            obs_fn = obs_fn+'[0]'
        fn_ext = os.path.split(obs_fn)[-1]
        if fn_ext[:3]!='DSN':
            obs_fns.append(obs_fn)
    return obs_fns

def get_y3a1(path):
    '''Downloads the Y3A1 catalogue in the DES-SN fields
    arguments:
    path (str): the path you want to save the catalogues'''

    import easyaccess as ea
    conn = ea.connect()
    fcents ={'C1':(54.2743,-27.1116),
    'C2':(54.2743,-29.0884),
    'C3':(52.6484,-28.1000),
    'E1':(7.8744,-43.0096),
    'E2':(9.5000,-43.9980),
    'S1':(42.8200,0.0000),
    'S2':(41.1944,-0.9884),
    'X1':(34.4757,-4.9295),
    'X2':(35.6645,-6.4121),
    'X3':(36.4500,-4.6000)}
    fields = ['E1','E2','S1','S2','C1','C2','C3','X1','X2','X3']
    for f in fields:
        ra,dec = fcents[f]
        RAMIN,RAMAX,DECMIN,DECMAX = ra-1.6,ra+1.6,dec-1.6,dec+1.6
        q = 'select Y3A1_COADD_OBJECT_SUMMARY.COADD_OBJECT_ID, RA, DEC, \
        MAGERR_AUTO_G, MAGERR_AUTO_R, MAGERR_AUTO_I, MAGERR_AUTO_Z,\
        MAGERR_DETMODEL_G, MAGERR_DETMODEL_R, MAGERR_DETMODEL_I, MAGERR_DETMODEL_Z,\
        MAG_AUTO_G, MAG_AUTO_R, MAG_AUTO_I, MAG_AUTO_Z, \
        MAG_DETMODEL_G, MAG_DETMODEL_R, MAG_DETMODEL_I, MAG_DETMODEL_Z, \
        SPREAD_MODEL_G, SPREAD_MODEL_R, SPREAD_MODEL_I, SPREAD_MODEL_Z, \
        SPREADERR_MODEL_G, SPREADERR_MODEL_R, SPREADERR_MODEL_I, SPREADERR_MODEL_Z, \
        FLAGS_G, FLAGS_I, FLAGS_R, FLAGS_Z \
        from Y3A1_COADD_OBJECT_SUMMARY where RA between %s and %s and DEC between %s AND %s and \
        flags_g = 0 and flags_r = 0 and flags_i = 0 and flags_z = 0 and \
        imaflags_iso_g = 0 and imaflags_iso_r = 0 and imaflags_iso_i = 0 and imaflags_iso_z = 0\
        order by DEC, RA'%(RAMIN,RAMAX,DECMIN,DECMAX)

        dat = conn.query_to_pandas(q)
        dat.to_csv(path+'/y3a1_%s_summary.csv'%f)

def resample(s,lst,y,chip,cuts,j,logger,stamp_sizex=4200,stamp_sizey=2200):
    '''Resamples a list of raw exposures so that they can be coadded'''

    img_list = np.genfromtxt(lst,dtype='str',delimiter='\n')
    if len(img_list)==0:
        logger.info('Empty list: %s \n %s'%(lst,img_list))
        return False

    starttime=float(time.time())
    logger.info('Creating weightmaps for individual input exposures in %s, %s band, chip %s'%(s.field,s.band,chip))
    weightlist,resamplist,headerlist,masklist  = [],[],[],[]
    os.chdir(s.temp_dir)
    #try:
    #    pixel_scale = 3600.0*abs(fits.getheader(img_list[0])['CD1_1'])
    #except:
        #pixel_scale = 3600.0*abs(fits.getheader(img_list[0][:-3])['CD1_1'])
    ra_cent,dec_cent = get_chip_vals(s.field,int(chip))
    try:
        if len(img_list)>1:

            for img in img_list:
                imgname = os.path.split(img)[-1]
                imgroot = imgname[:-5]
                if imgroot[-2:]=='fi':
                    imgroot = imgroot[:-3]
                weightlist.append(os.path.join(s.temp_dir,imgroot +'.resamp.weight.fits'))
                resamplist.append(os.path.join(s.temp_dir,imgroot+'.resamp.fits'))
                headerlist.append(os.path.join(s.temp_dir,imgroot+'.resamp.head'))
                masklist.append(os.path.join(s.temp_dir,imgroot+'.resamp.mask.fits'))

            swarp_cmd = [
            'swarp',
            '@%s'%lst,
            '-COMBINE','N',
            '-RESAMPLE','Y',
            #'-IMAGE_SIZE','%s,%s'%(4113,2058),
            #'-CENTER_TYPE','MANUAL',
            #'-CENTER','%f,%f'%(ra_cent,dec_cent),
            #'-PIXELSCALE_TYPE','MANUAL',
            #'-PIXEL_SCALE','0.2363',#'%.03f'%pixel_scale,
            '-BACK_SIZE','256',
            ]

    except TypeError:
        img = str(img_list)
        swarp_cmd = [
        'swarp','%s'%img,
        '-COMBINE','N',
        '-RESAMPLE','Y',
        '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
        '-CENTER_TYPE','MANUAL',
        '-CENTER','%f,%f'%(ra_cent,dec_cent),
        '-PIXELSCALE_TYPE','MANUAL',
        '-PIXEL_SCALE','0.2363',#'%.03f'%pixel_scale,
        '-BACK_SIZE','256',
        ]
        imgname = os.path.split(img)[-1]
        imgroot = imgname[:-5]
        if imgroot[-2:]=='fi':
            imgroot = imgroot[:-3]
        weightlist.append(os.path.join(s.temp_dir,imgroot +'.resamp.weight.fits'))
        resamplist.append(os.path.join(s.temp_dir,imgroot+'.resamp.fits'))
        headerlist.append(os.path.join(s.temp_dir,imgroot+'.resamp.head'))
        masklist.append(os.path.join(s.temp_dir,imgroot+'.resamp.mask.fits'))

    logger.info('Resampling with command: %s'%swarp_cmd)
    p = subprocess.Popen(swarp_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    outs,errs = p.communicate()
    endtime=float(time.time())
    logger.info('Finished creating weightmaps for %s, %s band, chip %s; took %.3f seconds'%(s.field,s.band,chip,endtime-starttime))

    weightlist = np.array(weightlist)
    weightlist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.wgt.lst'%(y,s.field,s.band,chip,s.cutstring,j))
    np.savetxt(weightlist_name,weightlist,fmt='%s')
    resamplist = np.array(resamplist)
    resamplist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.resamp.lst'%(y,s.field,s.band,chip,s.cutstring,j))
    np.savetxt(resamplist_name,resamplist,fmt='%s')
    headerlist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.head.lst'%(y,s.field,s.band,chip,s.cutstring,j))
    np.savetxt(headerlist_name,headerlist,fmt='%s')

    try:
        for img in resamplist:
            imgname = os.path.split(img)[-1]
            imgroot = imgname[:-5]

            if imgroot[-2:]=='fi':
                imgroot = imgroot[:-3]
            try:
                imgn = img[:-3]
                h = fits.getheader(imgn)
            except:
                h = fits.getheader(img)
            header_name = os.path.join(s.temp_dir,imgroot+'.head')
            if os.path.isfile(header_name):
                os.remove(header_name)
            h.totextfile(header_name)

    except TypeError:
        img = str(img_list)
        h = fits.getheader(img)
        h.totextfile(os.path.join(s.temp_dir,imgroot+'.head'))
    return (weightlist_name,resamplist_name,headerlist)

def resample_single_seaosn(s,lst,y,j,logger):
    '''Resamples a list of raw exposures so that they can be coadded'''

    img_list = np.genfromtxt(lst,dtype='str',delimiter='\n')
    if len(img_list)==0:
        logger.info('Empty list: %s \n %s'%(lst,img_list))
        return False

    starttime=float(time.time())
    logger.info('Creating weightmaps for individual input exposures for %i, %s band, Season %i'%(s.snid,s.band,y))
    weightlist,resamplist,headerlist,masklist  = [],[],[],[]
    os.chdir(s.temp_dir)
    #try:
    #    pixel_scale = 3600.0*abs(fits.getheader(img_list[0])['CD1_1'])
    #except:
        #pixel_scale = 3600.0*abs(fits.getheader(img_list[0][:-3])['CD1_1'])
    ra_cent,dec_cent = get_chip_vals(s.field,int(chip))
    try:
        if len(img_list)>1:

            for img in img_list:
                imgname = os.path.split(img)[-1]
                imgroot = imgname[:-5]
                if imgroot[-2:]=='fi':
                    imgroot = imgroot[:-3]
                weightlist.append(os.path.join(s.temp_dir,imgroot +'.resamp.weight.fits'))
                resamplist.append(os.path.join(s.temp_dir,imgroot+'.resamp.fits'))
                headerlist.append(os.path.join(s.temp_dir,imgroot+'.resamp.head'))
                masklist.append(os.path.join(s.temp_dir,imgroot+'.resamp.mask.fits'))

            swarp_cmd = [
            'swarp',
            '@%s'%lst,
            '-COMBINE','N',
            '-RESAMPLE','Y',
            #'-IMAGE_SIZE','%s,%s'%(4113,2058),
            #'-CENTER_TYPE','MANUAL',
            #'-CENTER','%f,%f'%(ra_cent,dec_cent),
            #'-PIXELSCALE_TYPE','MANUAL',
            #'-PIXEL_SCALE','0.2363',#'%.03f'%pixel_scale,
            '-BACK_SIZE','256',
            ]

    except TypeError:
        img = str(img_list)
        swarp_cmd = [
        'swarp','%s'%img,
        '-COMBINE','N',
        '-RESAMPLE','Y',
        '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
        '-CENTER_TYPE','MANUAL',
        '-CENTER','%f,%f'%(ra_cent,dec_cent),
        '-PIXELSCALE_TYPE','MANUAL',
        '-PIXEL_SCALE','0.2363',#'%.03f'%pixel_scale,
        '-BACK_SIZE','256',
        ]
        imgname = os.path.split(img)[-1]
        imgroot = imgname[:-5]
        if imgroot[-2:]=='fi':
            imgroot = imgroot[:-3]
        weightlist.append(os.path.join(s.temp_dir,imgroot +'.resamp.weight.fits'))
        resamplist.append(os.path.join(s.temp_dir,imgroot+'.resamp.fits'))
        headerlist.append(os.path.join(s.temp_dir,imgroot+'.resamp.head'))
        masklist.append(os.path.join(s.temp_dir,imgroot+'.resamp.mask.fits'))

    logger.info('Resampling with command: %s'%swarp_cmd)
    p = subprocess.Popen(swarp_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    outs,errs = p.communicate()
    endtime=float(time.time())
    logger.info('Finished creating weightmaps for %i, %s band, season %i; took %.3f seconds'%(s.snid,s.band,y,endtime-starttime))

    weightlist = np.array(weightlist)
    weightlist_name = os.path.join(s.list_dir, '%i_%i_%s_%s_%i.wgt.lst' % (s.snid, y, s.band, s.cutstring, j))
    resamplist_name = os.path.join(s.list_dir, '%i_%i_%s_%s_%i.resamp.lst' % (s.snid, y, s.band, s.cutstring, j))
    headerlist_name = os.path.join(s.list_dir, '%i_%i_%s_%s_%i.head.lst' % (s.snid, y, s.band, s.cutstring, j))
    np.savetxt(weightlist_name,weightlist,fmt='%s')
    resamplist = np.array(resamplist)
    np.savetxt(resamplist_name,resamplist,fmt='%s')
    np.savetxt(headerlist_name,headerlist,fmt='%s')

    try:
        for img in resamplist:
            imgname = os.path.split(img)[-1]
            imgroot = imgname[:-5]

            if imgroot[-2:]=='fi':
                imgroot = imgroot[:-3]
            try:
                imgn = img[:-3]
                h = fits.getheader(imgn)
            except:
                h = fits.getheader(img)
            header_name = os.path.join(s.temp_dir,imgroot+'.head')
            if os.path.isfile(header_name):
                os.remove(header_name)
            h.totextfile(header_name)

    except TypeError:
        img = str(img_list)
        h = fits.getheader(img)
        h.totextfile(os.path.join(s.temp_dir,imgroot+'.head'))
    return (weightlist_name,resamplist_name,headerlist)

def make_cap_stamps(sg,sr,si,sz,chip,sn_name,ra,dec,stamp_sizex=4100,stamp_sizey=2100):
    '''***Deprecated***
    Function to make a small stamp around an SN position to do common aperture photometry
    '''
    logger = logging.getLogger(__name__)
    logger.handlers =[]
    logger.setLevel(logging.DEBUG)
    formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    # resample to the same grid using SWarp
    # start by getting the science frames for each band

    sci_frames = []
    for s in [sg,sr,si,sz]:
        bd = s.band_dir
        # assume we don't have multiple versions of the science frame
        glob_string = os.path.join(bd,'ccd_%s_%s_*clipweighted_sci.fits'%(str(chip),s.band))
        logger.info("Looking for things that look like: '%s'"%glob_string)
        glob_list = glob.glob(glob_string)
        sci_frames.append(glob_list[0])
        logger.info("Found the correct coadd, exists at: '%s'"%glob_list[0])
    pixel_scale = 3600.0*abs(fits.getheader(sci_frames[0])['CD1_1'])
    sci_frame_str = sci_frames[0]+' '+sci_frames[1]+' '+sci_frames[2]+' '+sci_frames[3]

    # set up the directory if it doesn't already exist
    cap_dir = os.path.join(sg.out_dir,'CAP')
    if not os.path.isdir(cap_dir):
        os.mkdir(cap_dir)
    sn_dir = os.path.join(cap_dir,sn_name)
    if not os.path.isdir(sn_dir):
        os.mkdir(sn_dir)
    os.chdir(sn_dir)
    # make a white stamp as a det image
    logger.info('Resampling all bands in a stamp around %s'%sn_name)
    resamp_cmd = ['swarp',
    '-COMBINE','N',
    '-RESAMPLE','Y',
    '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
    '-CENTER_TYPE','MANUAL',
    '-CENTER','%f,%f'%(ra,dec),
    '-PIXELSCALE_TYPE','MANUAL',
    '-PIXEL_SCALE','%.03f'%pixel_scale,
    '-BACK_SIZE','512',
    sci_frame_str]
    logger.info("Swarping with the command:\n '%s'"%resamp_cmd)
    starttime=float(time.time())
    p = subprocess.Popen(resamp_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    outs,errs = p.communicate()
    endtime=float(time.time())
    logger.info('Done resampling a stamp around %s, took %.3f seconds'%(sn_name,endtime-starttime))

    # Now resample the  image
    resamp_frames = []
    for s in [sg,sr,si,sz]:
        glob_string = os.path.join(sn_dir,'ccd_%s_%s_*_sci.resamp.fits'%(str(chip),s.band))
        glob_list = glob.glob(glob_string)
        resamp_frames.append(glob_list[0])
        cmd = ['swarp',
        '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
        '-CENTER_TYPE','MANUAL',
        '-CENTER','%f,%f'%(ra,dec),
        '-PIXELSCALE_TYPE','MANUAL',
        '-PIXEL_SCALE','%.03f'%pixel_scale,
        '-BACK_SIZE','512',
        '-IMAGEOUT_NAME',glob_list[0],
        glob_list[0]]
        pr = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        out,errs = pr.communicate()
    resamp_frame_str = resamp_frames[0]+' '+resamp_frames[1]+' '+resamp_frames[2]+' '+resamp_frames[3]

    return

def get_chip_vals(f,chip,vals = 'center'):
    '''Function to get the ra, dec of the center or corners of one chip in a given field'''

    lim_file=open('/media/data3/wiseman/des/coadding/config/chiplims.pkl','rb')
    chiplims = cpickle.load(lim_file)
    short_field = f[-2:]
    field_lims = chiplims[short_field]
    this_chip_lims = field_lims[chip]
    if vals == 'center':
        ras = [this_chip_lims[i][0] for i in range(4)]
        decs = [this_chip_lims[i][1] for i in range(4)]
        return (np.mean(ras),np.mean(decs))
    elif vals == 'lims':
        return this_chip_lims

def resample_chip_for_cap(sg,sr,si,sz,chip,stamp_sizex=4300,stamp_sizey=2300,npix_off1 = 0,npix_off2 = 0):
    '''Function to resample the coadds in each band in order to create a detection image and do common aperture photometry'''

    logger = logging.getLogger(__name__)
    logger.handlers =[]
    logger.setLevel(logging.DEBUG)
    formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    # resample to the same grid using SWarp
    # start by getting the science frames for each band

    sci_frames = []
    naxis1s,naxis2s = [],[]
    for s in [sg,sr,si,sz]:
        bd = s.band_dir
        # assume we don't have multiple versions of the science frame
        glob_string = os.path.join(bd,'ccd_%s_%s_%s_clipweighted_sci.fits'%(str(chip),s.band,s.cutstring))
        logger.info("Looking for things that look like: '%s'"%glob_string)
        glob_list = glob.glob(glob_string)
        if len(glob_list)==0:
            logger.info('Failing on %s,%s'%(s.band,chip))
        sci_frames.append(glob_list[0])
        logger.info("Found the correct coadd, exists at: '%s'"%glob_list[0])
        naxis1s.append(fits.getheader(glob_list[0])['NAXIS1'])
        naxis2s.append(fits.getheader(glob_list[0])['NAXIS2'])
    ghead = fits.getheader(sci_frames[0])
    pixel_scale = 3600.0*abs(ghead['CD1_1'])
    sci_frame_str = sci_frames[0]+' ' +sci_frames[1]+' '+sci_frames[2]+' '+sci_frames[3]

    # set up the directory if it doesn't already exist
    cap_dir = os.path.join(sg.out_dir,'MY%s'%sg.my,sg.field,'CAP')
    if not os.path.isdir(cap_dir):
        os.mkdir(cap_dir)
    cap_chip_dir = os.path.join(cap_dir,str(chip))
    if not os.path.isdir(cap_chip_dir):
        os.mkdir(cap_chip_dir)
    os.chdir(cap_chip_dir)
    # find the center of the chip
    ra_cent,dec_cent = ghead['CRVAL1'],ghead['CRVAL2']
    smallest1,smallest2 = min(naxis1s),min(naxis2s)
    stamp_sizex,stamp_sizey = smallest1-80-npix_off1,smallest2-80-npix_off2
    # make a riz stamp as a det image
    logger.info('Resampling all bands in MY%s, %s, chip %s'%(sg.my,sg.field,chip))
    logger.info('Hopefully making images of size %s x %s'%(stamp_sizex,stamp_sizey))
    logger.info('Having taken %s off Axis 1 and % off Axis 2' %(npix_off1,npix_off2))
    resamp_cmd = ['swarp',
    '-COMBINE','N',
    '-RESAMPLE','Y',
    '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
    '-CENTER_TYPE','MANUAL',
    '-CENTER','%f,%f'%(ra_cent,dec_cent),
    '-PIXELSCALE_TYPE','MANUAL',
    '-PIXEL_SCALE','%.03f'%pixel_scale,
    '-BACK_SIZE','512',
    sci_frame_str]
    logger.info("Swarping with the command:\n '%s'"%resamp_cmd)
    starttime=float(time.time())
    p = subprocess.Popen(resamp_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    outs,errs = p.communicate()
    endtime=float(time.time())
    logger.info('Done resampling MY%s, %s, chip %s, took %.3f seconds'%(sg.my,sg.field,chip,endtime-starttime))
    # Now resample the  image
    resamp_frames = []
    for s in [sg,sr,si,sz]:
        glob_string = os.path.join(cap_chip_dir,'ccd_%s_%s_%s*_clipweighted*.resamp.fits'%(str(chip),s.band,s.cutstring))
        glob_list = glob.glob(glob_string)
        resamp_frames.append(glob_list[0])

    resamp_frame_str = resamp_frames[1]+' '+resamp_frames[2]+' '+resamp_frames[3]
    logger.info('Creating riz with size %s x %s'%(stamp_sizex,stamp_sizey))
    riz_cmd = ['swarp',
    '-IMAGE_SIZE','%s,%s'%(stamp_sizex,stamp_sizey),
    '-CENTER_TYPE','MANUAL',
    '-CENTER','%f,%f'%(ra_cent,dec_cent),
    '-PIXELSCALE_TYPE','MANUAL',
    '-PIXEL_SCALE','%.03f'%pixel_scale,
    '-COMBINE','Y',
    '-RESAMPLE','N',
    '-BACK_SIZE','512',
    '-IMAGEOUT_NAME','%s_%s_%s_riz.fits'%(sg.my,sg.field,chip),
    resamp_frame_str]
    logger.info("Making a detection image for MY%s, %s, chip %s for CAP"%(sg.my,sg.field,chip))
    logger.info("Using this command: %s"%riz_cmd)
    starttime=float(time.time())
    p = subprocess.Popen(riz_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    outs,errs = p.communicate()
    endtime=float(time.time())
    logger.info('Done making detection image for for MY%s, %s, chip %s, took %.3f seconds'%(sg.my,sg.field,chip,endtime-starttime))
    logger.info('Checking they are the correct size')
    n_off1,n_off2 = check_resamps('%s_%s_%s_riz.fits'%(sg.my,sg.field,chip),resamp_frames)
    return '%s_%s_%s_riz.fits'%(sg.my,sg.field,chip),n_off1,n_off2

def check_resamps(riz_fn,resamp_frames):
    '''Convenience function to check if a resample is too big'''

    riz_h = fits.getheader(riz_fn)
    n1riz,n2riz = riz_h['NAXIS1'],riz_h['NAXIS2']
    #print ('Length of riz: %s x %s'%(n1riz,n2riz))
    n_off1 = 0
    n_off2 = 0
    for i in range(len(resamp_frames)):
        print (resamp_frames[i])
        h = fits.getheader(resamp_frames[i])
        n1,n2 = h['NAXIS1'],h['NAXIS2']
        n_diff1 = n1riz - n1
        n_diff2 = n2riz - n2
        print (n_diff1,n_diff2)
        if n_diff1>0:
            if n_diff1>n_off1:
                n_off1 = n_diff1
        if n_diff2>0:
            if n_diff2>n_off2:
                n_off2 = n_diff2
    print ('Returning',n_off1*2,n_off2*2)
    return (n_off1*2,n_off2*2)

def get_cuts(f,b):
    '''Function that returns the adopted teff and psf cuts for a given field and band'''

    cp=configparser.ConfigParser()
    # read the .ini file
    cp.read('/media/data3/wiseman/des/coadding/config/snobs_params.ini')

    if f[-1]!='3':
        cuts = cp['%s_shallow'%b]
    else:
        cuts = cp['%s_deep'%b]
    cuts = dict(cuts)
    try:
        cuts['teff']=float(cuts['teff'])
    except:
        pass
    try:
        cuts['psf']=float(cuts['psf'])
    except:
        pass

    return cuts

def combine_mask_weight(s,chip,j):
    '''Function that combines the outlier masks and weightmaps'''

    maskweightlist,masklist = [],[]
    resamplist_name = os.path.join(s.list_dir,'%s_%s_%s_%s_%s_%s.resamp.lst'%(s.my,s.field,s.band,chip,s.cutstring,j))
    for f in np.genfromtxt(resamplist_name,dtype='str',delimiter='\n'):
        if f[-3:]=='[0]':
            f = f[:-3]
        #try:
        wn = os.path.join(s.temp_dir,f.replace('fits','weight.fits'))
        mn = os.path.join(s.temp_dir,f.replace('fits','head.mask.fits'))
        masklist.append(mn)
        w = fits.open(wn)
        wd = w[0].data
        m = fits.getdata(mn)
        try:
            maskweight = np.multiply(wd,m)
        except ValueError:
            if wd.shape[0]<m.shape[0] or wd.shape[1]<m.shape[1]:
                new_mask = m[:wd.shape[0],:wd.shape[1]]
            elif wd.shape[0]>m.shape[0] or wd.shape[1]>m.shape[1]:
                new_mask = np.ones_like(wd)
                new_mask[:m.shape[0],:m.shape[1]]=m
            maskweight = np.multiply(wd,new_mask)
        w[0].data = maskweight
        #print ('Weight size',w[0].header['NAXIS1'],w[0].header['NAXIS2'])
        w.writeto(os.path.join(s.temp_dir,f.replace('fits','maskweight.fits')),overwrite=True)
        maskweightlist.append(os.path.join(s.temp_dir,f.replace('fits','maskweight.fits')))

            #except:
                #wn = os.path.join(s.temp_dir,f.replace('fits','weight.fits'))
                #w = fits.open(wn)
                #w.writeto(os.path.join(s.temp_dir,f.replace('fits','maskweight.fits')),overwrite=True)
                #maskweightlist.append(os.path.join(s.temp_dir,f.replace('fits','maskweight.fits')))

    np.savetxt(resamplist_name.replace('resamp','maskweight'),np.array(maskweightlist),fmt='%s')
    for mn in masklist:
        os.remove(mn)
    return maskweightlist

def cut_out(fn,ra,dec,width,height,savename):

    '''Makes a cut-out of width and height in arcsec and saves to file savename'''
    from astropy.wcs import WCS
    import astropy.units as u
    pixscale=0.263
    hdu = fits.open(fn)
    h = hdu[0].header
    d = hdu[0].data
    wcs = WCS(h)
    cent_pixs = [int(x) for x in  wcs.world_to_pixel(ra*u.deg,dec*u.deg)]
    from astropy.nddata.utils import Cutout2D
    cutout = Cutout2D(d, position=cent_pixs, wcs=wcs, size=[width * u.arcsec,height*u.arcsec])
    hdu[0].data = cutout.data
    hdu[0].header.update(cutout.wcs.to_header())
    hdu[0].writeto(savename, overwrite=True)

def make_swarp_cmds_single_season(s,y):
    """function to make swarp command to stack Nminus1_year, field chip, band"""
    cuts = s.cuts
    if not os.path.isdir(os.path.join(s.out_dir,'%i'%s.snid,y,s.band)):
        os.mkdir(os.path.join(s.out_dir,'%i'%s.snid,y,s.band))
    logger = logging.getLogger(__name__)
    logger.handlers =[]
    logger.setLevel(logging.DEBUG)
    formatter =logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    logger.info('Preparing the frames for %i, season %i'%(s.snid,y))
    #band = band + '    '
    ## Get the list of exposures for MY, field, chip, band.
    good = s.good_frame
    good_band = good[good['BAND']==s.band]
    good_band_y = good_band[good_band['SEASON']!=int(y)]
    good_y_exps = good_band_y['EXPNUM'].unique()
    #for each good exposure, find it's file
    stack_fns = {}
    logger.info('Adding files to the %i, %s band, Y%i stack'%(snid,s.band,y))
    good_band_y.sort_values('CHIP_ZERO_POINT',ascending=False,inplace=True)
    n,l=0,0
    stack_fns[0]=[]
    nights = []
    for counter,exp in enumerate(good_y_exps):

        this_exp = good_band_y[good_band_y['EXPNUM']==exp]
        first = this_exp.iloc[0]
        night = str(first['NITE'])
        #chip = first['CCDNUM']

        this_exp_fn = get_dessn_obs(s,night,exp,chip,logger)
        #logger.info("Adding file from %s" %night)
        if night not in nights:
            if this_exp_fn:
                nights.append(night)
                for fn in this_exp_fn:
                    n+=1
                    stack_fns[l].append(fn)

                    if n%100.0 == 0:
                        l+=1
                        stack_fns[l]=[]
    cmd_list = {}
    for j in range(0,l+1):
        fns = np.array(stack_fns[j])
        fn_list = os.path.join(s.temp_dir,\
        'stack_fns_MY%s_%s_%s_%s_%s_%s.lst' %(MY,field,band,chip,s.cutstring,j))
        logger.info('Saving list of files to stack at {0}'.format(fn_list))
        np.savetxt(fn_list,fns,fmt='%s')

        fn_out = os.path.join(s.out_dir,'%i'%s.snid,y,s.band)+\
            '/%i_%i_%s_%s_%i_coadded.fits'%(s.snid,y,s.band,s.cutstring,j)
        weightlist_name = os.path.join(s.list_dir,'%i_%i_%s_%s_%i.wgt.lst'%(s.snid,y,s.band,s.cutstring,j))
        resamplist_name = os.path.join(s.list_dir,'%i_%i_%s_%s_%i.resamp.lst'%(s.snid,y,s.band,s.cutstring,j))
        headerlist_name = os.path.join(s.list_dir,'%i_%i_%s_%s_%i.head.lst'%(s.snid,y,s.band,s.cutstring,j))

        weightout_name = fn_out[:-4]+'wgt.fits'
        nofiles = 0
        if not os.path.isfile(headerlist_name):
            logger.info("%i, %i, %s band: Going to do resampling!"%(s.snid,y,s.band))
            try:
                # TODO: REWRITE RESAMPLE_SINGLE_SEASON
                weightlist_name,resamplist_name,headerlist_name = resample_single_season(s,fn_list,y,j,logger)
            except TypeError:
                logger.info("No files in list: %s" %fn_list)
                nofiles = 1
        else:
            logger.info("Header list exists: %s, \n Going to make initial stack."%headerlist_name)
        if os.path.isfile(fn_out.replace('clipped','weighted')):
            cmd_list[j] = (False,False,fn_out)
        else:
            if nofiles ==0:
                cliptab_name = os.path.join(s.temp_dir,'cliptabs','%i_%i_%s_%s_%i_clipped.tab'%(s.snid,y,s.band,s.cutstring,j))
                clip_sigs = {
                'SN-X1':15,
                'SN-X2':15,
                'SN-C1':15,
                'SN-C2':15,
                'SN-S1':15,
                'SN-S2':15,
                'SN-E1':15,
                'SN-E2':15,
                'SN-X3':10,
                'SN-C3':10,
                }
                swarp_clip = [
                'swarp',
                '@%s'%resamplist_name,
                '-RESAMPLE','N',
                '-IMAGEOUT_NAME',fn_out,
                '-CLIP_LOGNAME',cliptab_name,
                '-CLIP_SIGMA',str(clip_sigs[s.field])
                ]
                maskweightlist_name = resamplist_name.replace('resamp','maskweight')

                swarp_weight = [
                'swarp',
                '@%s'%resamplist_name,
                '-RESAMPLE', 'N',
                '-COMBINE_TYPE', 'WEIGHTED',
                '-WEIGHT_TYPE', 'MAP_WEIGHT',
                '-WEIGHT_IMAGE', '@%s'%maskweightlist_name,
                '-IMAGEOUT_NAME', fn_out.replace('clipped.fits','weighted.fits')
                ]
                if os.path.isfile(fn_out):
                    cmd_list[j] = (False,swarp_weight,fn_out)
                else:

                    cmd_list[j]=(swarp_clip,swarp_weight,fn_out)
            else:
                cmd_list[j]=(False,False,False)

    #logger.info(cmd_list)
    return cmd_list


# -*- coding: utf-8 -*-
"""
Created on Wed Aug 06 17:51:05 2014

@author: Gael P Alamancos
@email: gael.perez[at]upf.edu
"""

import fileMerger as joinFiles
import psiPerGene as psiPerIsoform
import psiCalculator as psiPerEvent
import eventGenerator as generateEvents
import eventClusterer as clusterAnalysis
import significanceCalculator as diffSplice
import logging
import argparse
import sys

description = "Description:\n\n" + \
              "SUPPA allows you to generate all the possible Alternative Splicing events from an annotation file, \n" \
              "calculate the PSI values per event, calculate differential splicing across multiple conditions \n" \
              "with replicates, and cluster events across conditions \n" \
              "For further information, see the help of each subcommand."

parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawTextHelpFormatter, prog='SUPPA')
# Version
parser.add_argument('-v','--version', action='version', version='%(prog)s 2.3')
subparsers = parser.add_subparsers()

# EventGenerator parser
eventGeneratorSubparser = subparsers.add_parser(
    "generateEvents", parents=[generateEvents.parser],
    help="Calculates the Alternative Splicing events from an annotation file.")
eventGeneratorSubparser.set_defaults(which="generateEvents")

# psiCalculator parser
psiCalculatorSubparser = subparsers.add_parser(
    "psiPerEvent", parents=[psiPerEvent.parser],
    help="Calculates the PSI value for each event previously generated.")
psiCalculatorSubparser.set_defaults(which="psiPerEvent")

# psiPerGene parser
psiPerGeneSubparser = subparsers.add_parser(
    "psiPerIsoform", parents=[psiPerIsoform.parser],
    help="Calculates the PSI value for each isoform.")
psiPerGeneSubparser.set_defaults(which="psiPerIsoform")

# significanceCalculator parser
significanceCalculatorSubparser = subparsers.add_parser(
    "diffSplice", parents=[diffSplice.parser],
    help="Calculates differentially spliced events across multiple conditions.")
significanceCalculatorSubparser.set_defaults(which="diffSplice")

# eventClusterer parser
eventClustererSubparser = subparsers.add_parser(
    "clusterEvents", parents=[clusterAnalysis.parser],
    help="Calculates clusters of events across conditions.")
eventClustererSubparser.set_defaults(which="clusterEvents")

# fileMerger parser
fileMergerSubparser = subparsers.add_parser(
    "joinFiles", parents=[joinFiles.parser],
    help="Join multiple tab separated files into a single file.")
fileMergerSubparser.set_defaults(which="joinFiles")

# Setting logging preferences
logger = logging.getLogger(__name__)

def main():

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    try:
        args = parser.parse_args()
        if args.which == "generateEvents":
            generateEvents.parser = parser  # Setting the module aparser
            generateEvents.main()
        elif args.which == "psiPerEvent":
            psiPerEvent.parser = parser  # Setting the module parser
            psiPerEvent.main()
        elif args.which == "psiPerIsoform":
            psiPerIsoform.parser = parser  # Setting the module parser
            psiPerIsoform.main()
        elif args.which == "diffSplice":
            diffSplice.parser = parser  # Setting the module parser
            diffSplice.main()
        elif args.which == "clusterEvents":
            clusterAnalysis.parser = parser  # Setting the module parser
            clusterAnalysis.main()
        elif args.which == "joinFiles":
            joinFiles.parser = parser  # Setting the module parser
            joinFiles.main()
    except Exception:
        logger.error("Unknown error: {}".format(sys.exc_info()))
        sys.exit(1)

if __name__ == '__main__':
    main()


import time
from datetime import datetime
import asyncio
import nextcord
from nextcord.ext import commands
from core.classes import Cog_Extension

routine = True
GRADUATE_DATE = datetime(2023, 6, 30)
WIFE_CHANNEL_ID = 1118725524974489770

class Routine(Cog_Extension):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @commands.Cog.listener()
    async def on_ready(self):
        try:
            WIFE_CHANNEL = self.bot.get_channel(WIFE_CHANNEL_ID)
            while routine:
                # get current time (date and time)
                now = time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())
                # get current time (only date)
                now_hr = time.strftime("%H", time.localtime())

                delta = datetime.now() - GRADUATE_DATE  # get delta time
                days, seconds = delta.days, delta.seconds  # convert seconds to days
                hours, seconds = divmod(seconds, 3600)  # convert seconds to hours
                minutes, seconds = divmod(seconds, 60)  # convert seconds to minutes
                delta_time = f"{days}天{hours}小時{minutes}分{seconds}秒"   # convert to str

                if now_hr == "08":
                    await WIFE_CHANNEL.send(f"現在是 {now}，距離賤臣老婆畢業已經過了 {delta_time} ，記得提醒賤臣醒來！")
                    print(f"<>老婆貓已經畢業 {delta_time} 了")
                await asyncio.sleep(3600)  # wait for 1 hour

        except Exception as err:
            await WIFE_CHANNEL.send(f"Routine catch error: ```{err}```")
            print(f">err< Routine: ```{err}```")

def setup(bot):
    bot.add_cog(Routine(bot))


import apache_beam as beam
import typing
import logging
from helper.argparser import RunParam
import glob
import boto3
from azure.storage.blob import BlobServiceClient
from helper.secret import get_secret
from source_sink import GCSConnector, SFTPConnector
import pandas as pd

class ListFiles(beam.DoFn):
    def _list_files_gcs(self, source: str) -> list:
        bucket_name = source.split('/')[2]
        path_components = source.split('/')
        file_path = '/'.join(path_components[3:-1])
        file_pattern = path_components[-1]
        connector = GCSConnector(bucket_name)
        return connector.list_file(file_path, file_pattern)

    def _list_file_s3(self, source: str, sink_connection_secret: str) -> list:
        secret = get_secret(sink_connection_secret)
        s3 = boto3.resource('s3', **secret)
        bucket_name = source.split('/')[2]
        interface = source.split('/')[0]
        bucket = s3.Bucket(bucket_name)
        obj_list = bucket.objects.filter(Prefix='/'.join(source.split('/')[3:]))
        return [f'{interface}://{bucket_name}/{obj.key}' for obj in obj_list]

    def _list_file_azure(self, source: str, sink_connection_secret: str) -> list:
        secret = get_secret(sink_connection_secret)
        blob_service_client = BlobServiceClient.from_connection_string(source, **secret)
        container_name = source.split('/')[2]
        interface = source.split('/')[0]
        container_client = blob_service_client.get_container_client(container_name)
        blob_list = container_client.list_blobs(name_starts_with='/'.join(source.split('/')[3:]))
        return [f'{interface}://{container_name}/{blob.name}' for blob in blob_list]

    def _list_file_sftp(self, source: str, sink_connection_secret: str) -> list:
        secret = get_secret(sink_connection_secret)
        connector = SFTPConnector(secret)
        path_components = source.split('/')
        file_path = "/".join(path_components[2:-1]) + "/"
        file_pattern = path_components[-1]
        connector.close_connection()
        return connector.list_files(file_path, file_pattern)

    def _list_file_local(self, source: str) -> list:
        return glob.glob(source)

    def _choose_storage_service(self, source: str, sink_connection_secret: str):
        if source.startswith('gs://'):
            return self._list_files_gcs(source)
        elif source.startswith(('s3://', 's3a://', 's3n://')):
            return self._list_file_s3(source, sink_connection_secret)
        elif source.startswith(('az://', 'abfs://', 'abfss://')):
            return self._list_file_azure(source, sink_connection_secret)
        elif source.startswith(('sftp://', 'ftp://')):
            return self._list_file_sftp(source, sink_connection_secret)
        else:
            return self._list_file_local(source)

    def process(self, context: RunParam):
        # Extract info from context
        source = context.source
        source_connection_secret = context.source_connection_secret
        files_list = self._choose_storage_service(source, source_connection_secret)
        logging.info(f"Found {len(files_list)} files matched with pattern {source}")
        return files_list

class ReadCsv(beam.DoFn):
    def __init__(self, source_sink: RunParam, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.source_sink = source_sink

    def process(self, context: str):
        # Extract info from attribute & context
        file_path = context
        run_param = self.source_sink

        # Extract info from run_param
        source_connection_secret = run_param.source_connection_secret
        pipeline_options = run_param.pipeline_options
        read_options = pipeline_options.get('read_options', {})

        # Read and return result as pd.DataFrame
        logging.info(f"Reading data from {file_path}")
        if file_path.startswith('sftp://'):
            file_path = file_path.replace('sftp://', '')
            connector = SFTPConnector(get_secret(source_connection_secret))
            df = connector.read_csv(file_path, read_options)
            connector.close_connection()
        else:
            df = pd.read_csv(file_path, **read_options)
        yield df


#!/usr/bin/python3
# <xbar.title>CoreAudio Samplerate Display</xbar.title>
# <xbar.version>v1.0</xbar.version>
# <xbar.author>Anthony Lauzon</xbar.author>
# <xbar.author.github>anthonylauzon</xbar.author.github>
# <xbar.desc>Displays current samplerate for an audio device.</xbar.desc>
# <xbar.dependencies>bash</xbar.dependencies>
# <xbar.abouturl></xbar.abouturl>

import json
import os

gFileName = "/Users/mmora/audiodevice.txt"

itemsWalked = 0
indentCount = 0
logOutput = False
gInUSBDeviceNode = False
incAmt = 0
hasidProductNameString = False
hasidVendorNameString = False
hasIOAudioEngineGlobalUniqueID = False
hasAudioClassNum = False
hasIOAudioDeviceName = False
gInUSBDeviceNode = False
gInIOAudioDevice = False
gInIOAudioEngine = False
AMSCommand = '| param1="/System/Applications/Utilities/Audio\ MIDI\ Setup.app" '

def printFoundItem(in_item,in_str):
    print(f'{in_item}: {in_str}')
# ----------------------------------------------------------------------------------------------------------------
# function for looking for our USB Audio components and properties
# ----------------------------------------------------------------------------------------------------------------

def findOurComponents(item, key, node):
    global hasidProductNameString
    global hasidVendorNameString
    global hasIOAudioEngineGlobalUniqueID
    global hasAudioClassNum
    global hasIOAudioDeviceName
    global gInUSBDeviceNode
    global gInIOAudioDevice
    global gInIOAudioEngine

    if '_name' in key:  # logic is wrong here. It should look for a key that is only in a USB audio device node then iterate finding values.
        keyContents = item

        for usb_items, usb_value in node.items():
            if isinstance(usb_value, bytes):
                value_str = usb_value.decode('latin-1')
            else:
                value_str = str(usb_value)

            if '_name' == usb_items:

                if value_str != "coreaudio_device":
                    print("---")
                    value_str += "| shell=open " + AMSCommand

                    printFoundItem('Name', value_str )
                continue

            if '_properties' == usb_items:
                if value_str == "coreaudio_default_audio_system_device":
                    printFoundItem(":bell: Default System Alert Device", "Yes")
                else:
                    printFoundItem("Properties", value_str)

                continue
            if 'coreaudio_input_source' == usb_items:
                if value_str == "spaudio_default":
                    value_str = "Default"
                printFoundItem("Input Source", value_str)
                continue
            if 'coreaudio_output_source' == usb_items:
                if value_str == "spaudio_default":
                    value_str = "Default"
                printFoundItem("Output Source", value_str)
                continue
            if 'coreaudio_device_transport' == usb_items:
                tmpStr = ""
                if value_str == "coreaudio_device_type_AVB":
                    tmpStr = "AVB"
                if value_str == "coreaudio_device_type_firewire":
                    tmpStr = "Firewire"
                if value_str == "coreaudio_device_type_usb":
                    tmpStr = "USB"
                if value_str == "coreaudio_device_type_displayport":
                    tmpStr = "DisplayPort"
                if value_str == "coreaudio_device_type_builtin":
                    tmpStr = "Built-in"

                printFoundItem("Transport", tmpStr)

                continue
            if 'coreaudio_default_audio_output_device' == usb_items:

                if value_str == "spaudio_yes":
                    printFoundItem(":speaker: Default Output Device", "Yes")

                continue
            if 'coreaudio_default_audio_input_device' == usb_items:

                if value_str == "spaudio_yes":
                    printFoundItem(":microphone: Default Input Device", "Yes")

                continue
            if 'coreaudio_default_audio_system_device' == usb_items:
                if value_str == "spaudio_yes":
                    printFoundItem("Default System Output Device", "Yes")
                continue
            if 'coreaudio_device_manufacturer' == usb_items:
                printFoundItem("Manufacturer", value_str)

                continue
            if 'coreaudio_device_srate' == usb_items:
                printFoundItem("Current Sample Rate", value_str)
                continue
            if 'coreaudio_device_output' == usb_items:
                printFoundItem("Output Channels", value_str)
                continue
            if 'coreaudio_device_input' == usb_items:
                printFoundItem("Input Channels", value_str)
                continue

    return

# ----------------------------------------------------------------------------------------------------------------
# function for walking the plist and looking for USB audio
# components in a ioreg dump in plist format
# ----------------------------------------------------------------------------------------------------------------
def walk(node):
    global itemsWalked
    global indentCount
    global logOutput
    global gInUSBDeviceNode
    global incAmt

    try:
        pad = ' '

        indentCount += incAmt
        for key, item in node.items():
            # print(indentCount * pad + '%s :' % key) if logOutput else None
            keyStr = indentCount * pad + '%s' % key
            if isinstance(item, dict):
                print('%s {' % keyStr) if logOutput else None
                walk(item)
                print(indentCount * pad + '};') if logOutput else None
            if isinstance(item, list):
                printKeyStr = True
                for index, val in enumerate(item):
                    if isinstance(val, dict):
                        print('%s: {' % keyStr) if logOutput else None
                        walk(val)
                        print(indentCount * pad + '};') if logOutput else None

                    else:
                        if printKeyStr:
                            print('%s:' % keyStr) if logOutput else None
                            printKeyStr = False
                        if isinstance(val, bytes):
                            theLen = val.__len__()
                            if theLen < 9:
                                valStr = val.hex()
                                print(
                                    (indentCount + incAmt) * pad + '%d): 0x%s' % (index, valStr)) if logOutput else None
                            else:
                                if theLen > 64:
                                    valStr = val.hex()[0:63] + '…'
                                else:
                                    valStr = val.hex()

                                print((indentCount + incAmt) * pad + '%d): size = %d 0x%s' % (
                                index, theLen, valStr)) if logOutput else None
                        else:
                            print((indentCount + incAmt) * pad + '%d): %s' % (index, val)) if logOutput else None
            else:
                if isinstance(item, bytes):
                    theLen = item.__len__()
                    if theLen < 9:
                        itemStr = item.hex()
                        print(f'{keyStr}: 0x{itemStr}') if logOutput else None
                    else:
                        if theLen > 64:
                            itemStr = item.hex()[0:63] + '…'
                        else:
                            itemStr = item.hex()

                        print(f'{keyStr}: size = {theLen} [0x{itemStr}]') if logOutput else None
                else:
                    if not isinstance(item, dict):
                        print(f"{keyStr}: {item}") if logOutput else None

            # main function to check for our USB audio Components
            findOurComponents(item, key, node)

            itemsWalked += 1
    except UnboundLocalError:
        print(traceback.format_exc())
    except:
        print(traceback.format_exc())
        print('in Walk Oops!', sys.exc_info()[0], 'occurred.')
        print('exception in walk')
        pass
    if indentCount > 0:
        indentCount -= incAmt

    return

data = os.popen('/usr/sbin/system_profiler -json SPAudioDataType').read()
devices = json.loads(data)
#print(devices)
print(":microphone: :speaker:")
walk(devices)

# with open(gFileName) as f:
#     data = f.read()
#     devices = json.loads(data)
#     #print(devices)
#
#     print(":microphone:")
#
#     walk(devices)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 21 11:07:57 2019

@author: mazzone
"""

import pandas as pd
import numpy as np
from nltk.tokenize import word_tokenize
from nltk import pos_tag
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.preprocessing import LabelEncoder
from collections import defaultdict
from nltk.corpus import wordnet as wn
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn import model_selection, naive_bayes, svm
from sklearn.metrics import accuracy_score

np.random.seed(500)

class_weight = {0: 1.8,
                1: 3.2,
                2: 1.,
                3: 1.2}

le =  ce.OneHotEncoder(return_df=False, handle_unknown="ignore")
training3D_matrix = joblib.load('../dataPreProcessing/matrix3D_ANSWER_word2vecNOLEMMA')
trainingSet = pd.read_csv('trainingSet1165.csv', sep = ',')
cat=trainingSet['categoria']
data=np.array(trainingSet['domanda'])
data= trainingSet['domanda']
print(data)
#label_encoder = LabelEncoder()
#integer_encoded = label_encoder.fit_transform(cat)
# binary encode
#onehot_encoder = OneHotEncoder(sparse=False)
#integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
#onehot_encoded = onehot_encoder.fit_transform(integer_encoded)

#test3D_matrix = we.wordEmbedding(frase)

gnb = GaussianNB()
print(cat.shape)
gnb.fit(training3D_matrix, cat)

#y_pred = gnb.predict (test3D_matrix)

y_TT = le.fit_transform(cat.tolist())

y_predTT = le.transform(y_pred.tolist())

roc = roc_auc_score ( y_TT , y_predTT )
roc_val = roc_auc_score ( y_TT , y_predTT )

print ( '\rroc-auc: %s - roc-auc_val: %s' % (str ( round ( roc , 4 ) ) , str ( round ( roc_val , 4 ) )) ,
                    end=100 * ' ' + '\n' )

print(classification_report(y_pred , '3' ))

acc = accuracy_score ( y_pred , '3' )
print ( " Acc:" , acc )

pre = precision_score ( y_pred , '3' , average='micro')
print ( " Pre:" , pre )

re = recall_score ( y_pred , '3' , average='micro')
print ( " Rec:" , re )

f1 = f1_score ( y_pred , '3' , average='micro')
print ( " F1:" , f1 )

#joblib.dump(m, "trained_model.dump")


# coding: utf-8

# # Python example for the symmetries of the cosine modulation function:

# # Take a modulated analysis filter bank with impulse responses:
# $$\normalsize  {h_k (n)=h(n)⋅cos(\frac{\pi}{4}⋅(k +0.5)⋅(n+0.5))}$$
# with a cosine modulation function like a DCT4. For subband k=0, for N=8 subbands, and for a length of 32, we get:

# In[1]:

get_ipython().magic('matplotlib inline')
import numpy as np
import matplotlib.pyplot as plt

c = np.cos(np.pi/8*0.5*(np.arange(32)+0.5))
plt.plot(c)

# ## This image visualizes the following symmetries:

# In[2]:

c[:8]

# ## The second block is identical to flipping and negating the first block:

# In[3]:

c[8:16]

# In[4]:

-np.flipud(c[0:8])

# ## The third block is identical to negating the first block:

# In[5]:

c[16:24]

# ## The last block is identical to flipping the first block:

# In[6]:

c[24:32]

# And so on. We see the following rule: **Every second block is flipped, and after 2 blocks we get a sign change.**
#
# This is not only true for the first subband, but also for all other subbands k.
#

Multirate_Signal_Processing/14_MultirateSigProcMDCTpy/ Symmetries of the cosine modulation function.py


import sqlite3

conn = sqlite3.connect('emailDB.sqlite')  # make a connection
cur = conn.cursor()  # send SQL command to the cursor and get the results back through the same cursor

cur.execute('DROP TABLE IF EXISTS Counts')  # use cur.execute('') for the SQL command

cur.execute('CREATE TABLE Counts (email TEXT, count INTEGER)')

fname = input('Enter file name: ')
if len(fname) < 1:
    fname = 'mbox1.txt'
fh = open(fname)
for line in fh:
    if not line.startswith('From: '):
        continue
    pieces = line.split()
    email = pieces[1]
    # If we just want to know the user name, use:  email.split('@')[1]

    #  this question mark is a placeholder
    #  And this is a way to basically make sure that we don't allow SQL injection
    cur.execute('SELECT count FROM Counts WHERE email = ?', (email, ))

    # another way to write the following code, see
    # https://www.coursera.org/learn/python-databases/lecture/18SJ4/worked-example-counting-email-in-a-database
    try:
        #  FYI: https://blog.csdn.net/Jian_Yun_Rui/article/details/73692331
        count = cur.fetchone()[0]  # take the count value, if None, then break
        cur.execute('UPDATE Counts SET count = count + 1 WHERE email = ?', (email, ))
    except:
        cur.execute('INSERT INTO Counts (email, count) VALUES (?, 1)', (email, ))
    conn.commit()

# print out contants of the table in emailDB in order

sqlstr = 'SELECT email, count FROM Counts ORDER BY count DESC LIMIT 10'
for row in conn.execute(sqlstr):
    print(row[0], row[1])

conn.close()  # close the connection


from datetime import datetime
from typing import Any
from CxAdmin.jsonable import JSONSerializable

class Skill(JSONSerializable):
    id: str
    name: str
    proficiency: int
    added: datetime

    def __init__(
        self,
        id: str,
        name: str,
        proficiency: int,
        added: datetime,
    ):
        self.id = id
        self.name = name
        self.proficiency = proficiency
        self.added = added

    @staticmethod
    def from_json(data: dict[str, Any]) -> "Skill":
        return Skill(
            id=data["id"],
            name=data["name"],
            proficiency=data["proficiency"],
            added=datetime.fromisoformat(data["added"][:-1]),
        )

    def to_json(self) -> dict[str, Any]:
        return {
            "id": self.id,
            "name": self.name,
            "proficiency": self.proficiency,
            "added": self.added.isoformat(),
        }


import numpy as np
from scipy.optimize import minimize

from .logger import logger

class Joaquin:

    def __init__(self, X, y, y_ivar, idx_map, frozen=None):
        self.X = X
        self.y = y
        self.y_ivar = y_ivar

        # Currently, stores parameter names and shapes
        self._param_info = {}

        # duh
        self._param_info['parallax_zpt'] = 1

        # the inv-var of the prior on the spectral components in beta
        self._param_info['L2_ivar'] = 1

        # linear coefficients (in the exp argument)
        self._param_info['beta'] = self.X.shape[1]

        self.idx_map = idx_map
        if 'spec' in idx_map:
            L2_slice = self.idx_map['spec']
        else:
            L2_slice = np.ones(self.X.shape[1], dtype=bool)
        self.L2_slice = L2_slice

        if frozen is None:
            frozen = {}
        self.frozen = frozen

    @classmethod
    def from_data(self, config, data, **kwargs):
        X, idx_map = data.get_X(phot_names=config.phot_names)
        y = data.stars['parallax']
        y_ivar = 1 / data.stars['parallax_error'] ** 2
        return Joaquin(X, y, y_ivar, idx_map, **kwargs)

    def unpack_pars(self, par_list):
        i = 0
        par_dict = {}
        for key, par_len in self._param_info.items():
            if key in self.frozen:
                par_dict[key] = self.frozen[key]
            else:
                par_dict[key] = np.squeeze(par_list[i:i+par_len])
                i += par_len

        return par_dict

    def pack_pars(self, par_dict):
        parvec = []
        for i, k in enumerate(self._param_info):
            if k not in self.frozen:
                parvec.append(np.atleast_1d(par_dict[k]))
        return np.concatenate(parvec)

    def init_beta(self, parallax_zpt=None, L2_ivar=None, **_):
        parallax_zpt = self.frozen.get('parallax_zpt', parallax_zpt)
        L2_ivar = self.frozen.get('L2_ivar', L2_ivar)

        if parallax_zpt is None or L2_ivar is None:
            raise ValueError('todo')

        y = self.y + parallax_zpt
        plx_mask = y > (3 / np.sqrt(self.y_ivar))  # 3 sigma

        X = self.X[plx_mask]
        y = y[plx_mask]
        y_ivar = self.y_ivar[plx_mask]

        ln_plx_ivar = y**2 * y_ivar
        ln_y = np.log(y)

        XT_Cinv = X.T * ln_plx_ivar
        XT_Cinv_X = np.dot(XT_Cinv, X)
        XT_Cinv_X[np.diag_indices(X.shape[1])] += L2_ivar

        beta = np.linalg.solve(XT_Cinv_X, np.dot(XT_Cinv, ln_y))
        return beta

    def init(self, pack=True, **kwargs):
        init_dict = kwargs.copy()

        if 'beta' not in self.frozen:
            init_dict['beta'] = self.init_beta(**kwargs)

        if 'parallaz_zpt' not in self.frozen:
            init_dict['parallax_zpt'] = kwargs.get(
                'parallax_zpt', -0.03)  # MAGIC NUMBER (Gaia value)

        if pack:
            return self.pack_pars(init_dict)
        else:
            return init_dict

    @classmethod
    def model_y(cls, X, *, beta, **_):
        model_ln_plx = np.dot(X, beta)
        model_y = np.exp(model_ln_plx)
        return model_y

    def chi(self, *, parallax_zpt, beta, **_):
        y = self.y + parallax_zpt
        # model_ln_plx = np.dot(self.X, beta)
        # model_y = np.exp(model_ln_plx)
        model_y = self.model_y(self.X, parallax_zpt=parallax_zpt, beta=beta)
        resid = y - model_y
        return resid * np.sqrt(self.y_ivar)

    def ln_likelihood(self, *, parallax_zpt, beta, **_):
        ll_grad = {}

        y = self.y + parallax_zpt

        # model_ln_plx = np.dot(self.X, beta)
        # model_y = np.exp(model_ln_plx)
        model_y = self.model_y(self.X, parallax_zpt=parallax_zpt, beta=beta)
        resid = y - model_y

        ll = -0.5 * np.sum(resid**2 * self.y_ivar)

        if 'beta' not in self.frozen:
            ll_grad['beta'] = np.dot(self.X.T,
                                     model_y * self.y_ivar * resid)

        if 'parallax_zpt' not in self.frozen:
            ll_grad['parallax_zpt'] = \
                - np.sum((y + parallax_zpt - model_y) * self.y_ivar)

        return ll, ll_grad

    def ln_prior(self, *, parallax_zpt, L2_ivar, beta, **_):
        lp = 0.
        lp_grad = {}

        if 'beta' not in self.frozen:
            lp += - 0.5 * L2_ivar * np.sum(beta[self.L2_slice] ** 2)
            lp_grad['beta'] = np.zeros_like(beta)
            lp_grad['beta'][self.L2_slice] = - L2_ivar * beta[self.L2_slice]

        if 'parallax_zpt' not in self.frozen:
            plx_zpt_var = 0.1 ** 2  # MAGIC NUMBER
            lp += - 0.5 * parallax_zpt ** 2 / plx_zpt_var
            lp_grad['parallax_zpt'] = -1 / plx_zpt_var * parallax_zpt

        return lp, lp_grad

    def neg_ln_posterior(self, parallax_zpt, L2_ivar, beta):
        ll, ll_grad = self.ln_likelihood(
            parallax_zpt=parallax_zpt,
            beta=beta)

        lp, lp_grad = self.ln_prior(
            L2_ivar=L2_ivar,
            parallax_zpt=parallax_zpt,
            beta=beta)

        lp_grad = np.concatenate([np.atleast_1d(lp_grad[k])
                                  for k in self._param_info
                                  if k not in self.frozen])
        ll_grad = np.concatenate([np.atleast_1d(ll_grad[k])
                                  for k in self._param_info
                                  if k not in self.frozen])

        logger.log(0, f'objective function evaluation: ll={ll}, lp={lp}')
        return - (ll + lp), - (ll_grad + lp_grad)

    def __call__(self, p):
        par_dict = self.unpack_pars(p)
        return self.neg_ln_posterior(**par_dict)

    def optimize(self, init=None, **minimize_kwargs):
        """
        To set the maximum number of function evaluations, pass:

            options={'maxfun': ...}

        """
        if init is None:
            init = {}

        init.setdefault('parallax_zpt', 0.)
        init.setdefault('L2_ivar', 1.)

        if 'beta' not in init:
            init['beta'] = self.init_beta(**init)

        x0 = self.pack_pars(init)

        minimize_kwargs.setdefault('method', 'L-BFGS-B')
        if minimize_kwargs['method'] == 'L-BFGS-B':
            minimize_kwargs.setdefault('options', {'maxfun': 1024})

        res = minimize(
            self,
            x0=x0,
            jac=True,
            **minimize_kwargs)

        return res


from Bio import SeqIO
from differences import diff
from sys import argv

prot_name: str = argv[1]
out = open(prot_name + ".tsv", "w")
out.write("index\tstart\tend\tref\talt\tlocation\tdate\tproteome_id\n")

records = SeqIO.parse(f"{prot_name}.fasta", "fasta")
ref = next(records)
ref_seq: str = ref.seq

gen_index = 1
mut_index = 1
for record in records:
    annotations = record.name.split("|")
    meta = ()
    try:
        meta = (annotations[1].split("/")[1], annotations[2], f"{prot_name}{gen_index}")
        gen_index += 1
    except Exception:
        continue

    tups = diff(ref_seq, record.seq)
    for tup in tups:
        out.write("\t".join([str(mut_index), str(tup[0]), str(tup[1]), tup[2], tup[3], *meta]) + "\n")
        mut_index += 1

out.close()


import re
from abc import ABC, abstractmethod
import typing as ty
import ast

import lava.magma.core.learning.string_symbols as str_symbols

class Symbol(ABC):
    """Super class for all possible symbols."""

    def __init__(self, expr: ty.Optional[str] = "") -> None:
        """Initialize a Symbol.

        Parameters
        ----------
        expr : str
            String expression.
        """
        self._expr = expr

    @property
    def expr(self) -> str:
        """Get expression of the Symbol.

        Returns
        ----------
        expr : str
            String expression.
        """
        return self._expr

    @expr.setter
    def expr(self, expr: str) -> None:
        """Set expression of the Symbol.

        Parameters
        ----------
        expr : str
            String expression.
        """
        self._expr = expr

    @abstractmethod
    def __str__(self):
        pass

    @staticmethod
    def find_expr(expr: str,
                  reg_expr: str,
                  symbol: "Symbol") -> ty.Tuple[ty.Optional["Symbol"], str]:
        """Factory method for creating symbols.

        Matches an expression to a regular expression and if there is a match,
        return a symbol of the matching part of the expression
        as well as the rest of the expression.

        Parameters
        ----------
        expr : str
            String expression.
        reg_expr: str
            Regular expression.
        symbol: Symbol
            Uninitialized symbol

        Returns
        ----------
        symbol : Symbol, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        p = re.compile(reg_expr)
        m = p.match(expr)

        if m is None:
            return None, expr

        match_len = m.span()[1]
        symbol.expr = m.group()

        return symbol, expr[match_len:]

class SymbolList(Symbol):
    """Represents as list of Symbols."""

    def __init__(self):
        super(SymbolList, self).__init__('')

        self._list = []

    @property
    def list(self) -> ty.List[Symbol]:
        """Get list of the SymbolList.

        Returns
        ----------
        list : list
            List of Symbol objects.
        """
        return self._list

    def append(self, symbol: Symbol) -> None:
        """Append a Symbol to the SymbolList's list and the Symbol's
        expression to the SymbolList's expression.

        Parameters
        ----------
        symbol : Symbol
            Symbol object.
        """
        self._list.append(symbol)
        self._expr += symbol.expr

    def __str__(self) -> str:
        res = "["

        for idx, symbol in enumerate(self.list):
            res = res + symbol.__str__()
            if idx != len(self.list) - 1:
                res = res + ", "

        res = res + "]"

        return res

class Operator(Symbol):
    """Abstract super class for operator Symbols."""

class Addition(Operator):
    """Symbol representing the addition operator."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Addition"], str]:
        r"""Factory method for creating Addition symbols.

        Matches an expression to the regular expressions
        "\+".

        Return a Addition Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Addition, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        return Symbol.find_expr(expr, r"\+", Addition())

    def __str__(self):
        return "Addition(" + self._expr + ")"

class Subtraction(Operator):
    """Symbol representing the subtraction operator."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Subtraction"], str]:
        r"""Factory method for creating Subtraction symbols.

        Matches an expression to the regular expressions
        "\-".

        Return a Subtraction Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Subtraction, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        return Symbol.find_expr(expr, r'\-', Subtraction())

    def __str__(self):
        return "Subtraction(" + self._expr + ")"

class Multiplication(Operator):
    """Symbol representing the multiplication operator."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Multiplication"], str]:
        r"""Factory method for creating Multiplication symbols.

        Matches an expression to the regular expressions
        "\*".

        Return a Multiplication Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Multiplication, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        return Symbol.find_expr(expr, r'\*', Multiplication())

    def __str__(self):
        return "Multiplication(" + self._expr + ")"

class FactorSym(Symbol):
    """Abstract super class for factor Symbols."""

class Dependency(FactorSym):
    """Abstract super class for dependency Symbols."""

class X0(Dependency):
    """Symbol representing the x0 dependency."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["X0"], str]:
        """Factory method for creating X0 symbols.

        Matches an expression to the regular expressions
        "x0".

        Return a X0 Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : X0, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        return Symbol.find_expr(expr, str_symbols.X0, X0())

    def __str__(self):
        return "X0(" + self._expr + ")"

class Y0(Dependency):
    """Symbol representing the y0 dependency."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Y0"], str]:
        """Factory method for creating Y0 symbols.

        Matches an expression to the regular expressions
        "y0".

        Return a Y0 Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Y0, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        return Symbol.find_expr(expr, str_symbols.Y0, Y0())

    def __str__(self):
        return "Y0(" + self._expr + ")"

class Uk(Dependency):
    """Symbol representing the uk dependency."""

    def __init__(self):
        super().__init__()
        self._decimate_exponent = None

    @property
    def decimate_exponent(self) -> ty.Optional[int]:
        """Get decimate exponent of this Uk.

        Returns
        ----------
        decimate_exponent : int
            Decimate exponent.
        """
        return self._decimate_exponent

    @decimate_exponent.setter
    def decimate_exponent(self, decimate_exponent: int) -> None:
        """Set decimate exponent of this Uk.

        Parameters
        ----------
        decimate_exponent : int
            Decimate exponent.
        """
        self._decimate_exponent = decimate_exponent

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Uk"], str]:
        r"""Factory method for creating Uk symbols.

        Matches an expression to the regular expressions
        "u\d".

        Return a Uk Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Uk, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        symbol, expr = Symbol.find_expr(expr, r'u\d', Uk())

        if symbol is not None:
            symbol.decimate_exponent = int(symbol.expr[1:])

        return symbol, expr

    def __str__(self):
        return "Uk(" + self._expr + ")"

class Variable(FactorSym):
    """Symbol representing traces and synaptic variable factors."""

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["Variable"], str]:
        """Factory method for creating Variable symbols.

        Matches an expression to the regular expressions
        "x[12]", "y[123]", "w", "d", "t".

        Return a Variable Symbol if there is a match and the rest of the
        expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Variable, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        v = Variable()

        symbol, expr = Symbol.find_expr(expr, 'x[12]', v)
        if not isinstance(symbol, Variable):
            symbol, expr = Symbol.find_expr(expr, 'y[123]', v)
        if not isinstance(symbol, Variable):
            symbol, expr = Symbol.find_expr(expr, str_symbols.W, v)
        if not isinstance(symbol, Variable):
            symbol, expr = Symbol.find_expr(expr, str_symbols.D, v)
        if not isinstance(symbol, Variable):
            symbol, expr = Symbol.find_expr(expr, str_symbols.T, v)

        return symbol, expr

    def __str__(self):
        return "Variable(" + self._expr + ")"

class Expression(FactorSym):
    """Abstract super class for multi-symbol Symbols."""

    def __init__(self):
        super().__init__()
        self._sub_expr = None
        self._symbol_list = None

    @property
    def sub_expr(self) -> ty.Optional[str]:
        """Get sub-expression of this Expression.

        Returns
        ----------
        sub_expr : str
            String sub-expression.
        """
        return self._sub_expr

    @sub_expr.setter
    def sub_expr(self, sub_expr: str) -> None:
        """Set sub-expression of this Expression.

        Parameters
        ----------
        sub_expr : str
            String sub-expression.
        """
        self._sub_expr = sub_expr

    @property
    def symbol_list(self) -> ty.Optional[SymbolList]:
        """Get SymbolList associated with sub-expression of this Expression.

        Returns
        ----------
        symbol_list : SymbolList
            SymbolList.
        """
        return self._symbol_list

    @symbol_list.setter
    def symbol_list(self, symbol_list: SymbolList) -> None:
        """Set SymbolList associated with sub-expression of this Expression.

        Parameters
        ----------
        symbol_list : SymbolList
            SymbolList.
        """
        self._symbol_list = symbol_list

    @staticmethod
    def _find_closing_bracket_idx(expr: str) -> ty.Optional[int]:
        """Find closing bracket associated with the first found opening bracket
        and return its index.

        Return None if closing index was found matching the first found
        opening bracket.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        closing_bracket_idx : int
            Index of closing bracket.
        """
        idx = [(1 if i.group() == '(' else -1, i.start()) for i in
               re.finditer('[()]', expr)]
        sum_res = 0
        closing_idx = None

        for i in idx:
            sum_res += i[0]
            if sum_res == 0:
                closing_idx = i[1]
                break

        return closing_idx

    @abstractmethod
    def find_sub_expr(self, expr: str) -> str:
        pass

class BracketExpression(Expression):
    """Symbol representing a bracket expression of the form : (...)."""

    def find_sub_expr(self, expr: str) -> str:
        """Find sub-expression of an expression, assuming the expression is
        of the form "(...)", representing a BracketExpression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        sub_expr : str
            String sub-expression.
        """
        try:
            idx = self._find_closing_bracket_idx(expr)
            self._sub_expr = expr[1:idx]

            expr = expr[idx + 1:]

            return expr
        except TypeError:
            raise ValueError("Bracket expression missing closing bracket.")

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["BracketExpression"], str]:
        r"""Factory method for creating BracketExpression symbols.

        Matches an expression to the regular expression
        "\(". If there is a match, find the sub expression.

        Return a BracketExpression Symbol if there is a match and
        closing bracket and the rest of the expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : BracketExpression, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        init_expr = expr

        symbol, expr = Symbol.find_expr(expr, r'\(', BracketExpression())

        if isinstance(symbol, BracketExpression):
            expr = symbol.find_sub_expr('(' + expr)

            symbol.expr = init_expr

        return symbol, expr

    def __str__(self):
        return "BracketExpression(" + self._symbol_list.__str__() + ")"

class SgnExpression(Expression):
    """Symbol representing a sign expression of the form :  sgn(...)."""

    def find_sub_expr(self, expr: str) -> str:
        """Find sub-expression of an expression, assuming the expression is
        of the form "sgn(...)", representing a SgnExpression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        sub_expr : str
            String sub-expression.
        """
        try:
            idx = self._find_closing_bracket_idx(expr)
            self.sub_expr = expr[4:idx]

            expr = expr[idx + 1:]

            return expr
        except TypeError:
            raise ValueError("Bracket expression missing closing bracket.")

    @staticmethod
    def find(expr: str) -> ty.Tuple[ty.Optional["SgnExpression"], str]:
        r"""Factory method for creating SgnExpression symbols.

        Matches an expression to the regular expression
        "sgn\(". If there is a match, find the sub expression.

        Return a SgnExpression Symbol if there is a match and
        closing bracket and the rest of the expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : SgnExpression, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        init_expr = expr

        symbol, expr = Symbol.find_expr(expr, r'sgn\(', SgnExpression())

        if isinstance(symbol, SgnExpression):
            expr = symbol.find_sub_expr('sgn(' + expr)

            symbol.expr = init_expr

        return symbol, expr

    def __str__(self):
        return "SgnExpression(" + self._symbol_list.__str__() + ")"

class Literal(Symbol):
    """Symbol representing a literal."""

    def __init__(self):
        super().__init__()
        self._mantissa = 0
        self._base = 1
        self._exponent = 0
        self._literal_type = None

    @property
    def mantissa(self) -> int:
        """Get mantissa of this Literal.

        Returns
        ----------
        mantissa : int
            Mantissa of this literal.
        """
        return self._mantissa

    @property
    def base(self) -> int:
        """Get base of this Literal.

        Returns
        ----------
        base : int
            Base of this literal.
        """
        return self._base

    @property
    def exponent(self) -> int:
        """Get exponent of this Literal.

        Returns
        ----------
        exponent : int
            Exponent of this literal.
        """
        return self._exponent

    @property
    def literal_type(self) -> int:
        """Get literal type of this Literal.

        Returns
        ----------
        literal_type : int
            Literal type of this literal.
        """
        return self._literal_type

    @literal_type.setter
    def literal_type(self, literal_type: int) -> None:
        """Set literal type of this Literal.

        Parameters
        ----------
        literal_type : int
            Literal type of this literal.
        """
        self._literal_type = literal_type

    def to_int(self) -> None:
        """Extract mantissa, base and exponent of this Literal from string
        expression and store them."""
        if self._literal_type == 0:
            # Convert [+/-]x*2^[+/0]y
            val = self._expr.split('^')
            self._mantissa = ast.literal_eval(val[0][:-2])
            self._base = 2
            self._exponent = ast.literal_eval(val[1])
        elif self._literal_type == 1:
            # Convert [+/-]2^[+/0]y
            val = self._expr.split('^')
            self._mantissa = int(ast.literal_eval(val[0]) / 2)
            self._base = 2
            self._exponent = ast.literal_eval(val[1])
        elif self._literal_type == 2:
            # Convert [+/-]x
            self._mantissa = ast.literal_eval(self._expr)
            self._base = 1
            self._exponent = 0

    @property
    def val(self) -> int:
        """Get the integer value represented by this Literal.

        Returns
        ----------
        val : int
            Integer value of this Literal.
        """
        return int(self._mantissa * self._base ** self._exponent)

    @staticmethod
    def find(expr):
        r"""Factory method for creating Literal symbols.

        Matches an expression to the regular expressions
        "[\+\-]?\d+\*2\^[\+\-]?\d+", "[\+\-]?2\^[\+\-]?\d+", "[\+\-]?\d+".

        Return a Literal Symbol if there is a match and
        closing bracket and the rest of the expression.

        Parameters
        ----------
        expr : str
            String expression.

        Returns
        ----------
        symbol : Literal, optional
            Symbol matching regular expression.
        expr : str
            Remaining string expression after extraction of the symbol.
        """
        # Match x*2^y
        symbol, expr = Symbol.find_expr(expr, r'[\+\-]?\d+\*2\^[\+\-]?\d+',
                                        Literal())
        if isinstance(symbol, Literal):
            symbol.literal_type = 0
        else:
            # Match 2^y
            symbol, expr = Symbol.find_expr(expr, r'[\+\-]?2\^[\+\-]?\d+',
                                            Literal())
            if isinstance(symbol, Literal):
                symbol.literal_type = 1
            else:
                # Match x
                symbol, expr = Symbol.find_expr(expr, r'[\+\-]?\d+',
                                                Literal())
                if isinstance(symbol, Literal):
                    symbol.literal_type = 2

        if isinstance(symbol, Literal):
            symbol.to_int()

        return symbol, expr

    def __str__(self):
        return "Literal(" + self._expr + ")"

SYMBOL_CLASSES = [Addition, Subtraction, Multiplication, X0, Y0, Uk,
                  Variable, BracketExpression, SgnExpression, Literal]

class SymbolicEquation:
    """The SymbolicEquation represents a learning rule as a set of symbols.

    It provides means to generate a SymbolicEquation from a string following
    a fixed syntax.

    Parameters
    ----------
    target : str
        Target of the learning rule to be represented by this SymbolicEquation.
    str_learning_rule : str
        Learning rule in string format to be represented by
        this SymbolicEquation.
    """

    def __init__(self, target: str, str_learning_rule: str) -> None:
        self._target = target
        self._str_learning_rule = str_learning_rule

        self._symbol_list = self._generate_symbol_list_from_string()

    @property
    def target(self) -> str:
        """Get target of this SymbolicEquation.

        Returns
        ----------
        target : str
            Target of this SymbolicEquation.
        """
        return self._target

    @property
    def symbol_list(self) -> SymbolList:
        """Get SymbolList of this SymbolicEquation.

        Returns
        ----------
        symbol_list : SymbolList
            SymbolList of this SymbolicEquation.
        """
        return self._symbol_list

    def _find_next_symbol(self, expression: str) \
            -> ty.Tuple[ty.Optional[Symbol], str]:
        """Find next symbol in a string expression.

        If the new symbol is of type Expression, apply parse to
        its sub-expression.

        Parameters
        ----------
        expression : str
            String expression.

        Returns
        ----------
        new_symbol : Symbol
            Found new symbol.
        expression : str
            Remaining string expression after extraction of the new symbol.
        """
        new_symbol = None

        for symbol in SYMBOL_CLASSES:
            new_symbol, expression = symbol.find(expression)

            if isinstance(new_symbol, Expression):
                new_symbol.symbol_list = self._parse(new_symbol.sub_expr)

            if new_symbol is not None:
                break

        return new_symbol, expression

    def _parse(self, expression: str) -> SymbolList:
        """Parse a string expression to yield a SymbolList representation.

        Finds next symbols in the string expression one by one until
        the string expression is empty.

        Parameters
        ----------
        expression : str
            String expression.

        Returns
        ----------
        symbol_list : SymbolList
            SymbolList object resulting from the string parsing operation.
        """
        done = False
        symbol_list = SymbolList()

        while not done:
            new_symbol, expression = self._find_next_symbol(expression)
            symbol_list.append(new_symbol)
            done = not (len(expression) > 0)

        return symbol_list

    def _generate_symbol_list_from_string(self) -> SymbolList:
        """Generate a SymbolList representation from a string representation.

        Returns
        ----------
        symbol_list : SymbolList
            Learning rule in SymbolList representation.
        """
        learning_rule = re.sub(' ', '', self._str_learning_rule)

        symbol_list = self._parse(learning_rule)

        return symbol_list


from collections import deque

N, K = map(int, input().split())
MAX = 100001
visited = [0] * MAX

def BFS(N):
    q = deque()
    q.append(N)
    visited[N] = 0
    while q:
        N = q.popleft()
        if N == K:
            return visited[N]
        for num in (N - 1, N + 1, N * 2):
            if 0 <= num < MAX and visited[num] == 0:
                visited[num] = visited[N] + 1
                q.append(num)
print(BFS(N))