''' Plot script WC parameter LogLikelihood
'''

# Standard imports
import sys
import ROOT
import imp
import pickle
import ctypes
import numpy as np

from math import sqrt
# turn off graphics
ROOT.gROOT.SetBatch( True )

# RootTools
from RootTools.core.standard import *

from plot_helpers import getUncertaintyValue, getObservationValue
from multiprocessing import Pool

# Logger
import TTXPheno.Tools.logger as logger
import RootTools.core.logger as logger_rt
logger    = logger.get_logger(   'DEBUG', logFile = None)
logger_rt = logger_rt.get_logger('INFO', logFile = None)

# TTXPheno
from TTXPheno.samples.benchmarks import *
from TTXPheno.Tools.user import plot_directory, cardfileLocation

# get the reweighting function
from TTXPheno.Tools.WeightInfo import WeightInfo

ROOT.gStyle.SetNumberContours(255)

# Arguments
import argparse

argParser = argparse.ArgumentParser(description = "Argument parser")
argParser.add_argument('--version',            action='store',     default='test', help='Appendix to plot directory')
argParser.add_argument('--process',            action='store',     default='ttZ_3l', nargs='?', choices=['ttZ_3l', 'ttZ_4l', 'ttgamma_1l', 'ttgamma_2l'], help="which process to calculate?")
argParser.add_argument('--bestFit',            action='store_true', help='Run combine with bestFit scenario (wide r ranges)')
argParser.add_argument('--removeCardFiles',    action='store_true', help='remove cardfiles after calculation?')
argParser.add_argument('--useCombine',         action='store_true', help='use Higgs Combine Tool?')
argParser.add_argument('--fitOnly',            action='store_true', help='do you already have the cardfiles?')
argParser.add_argument('--sample',             action='store',     default='fwlite_ttZ_ll_LO_order2_15weights_ref', help='Sample name specified in sample/python/benchmarks.py, e.g. fwlite_ttZ_ll_LO_order2_15weights_ref')
argParser.add_argument('--order',              action='store',     default=2, help='Polynomial order of weight string (e.g. 2)')
argParser.add_argument('--selection',          action='store',     default='lepSel3-onZ-njet3p-nbjet1p-Zpt0', help="Specify cut.")
argParser.add_argument('--small',              action='store_true', help='Run only on a small subset of the data?')
argParser.add_argument('--contours',           action='store_true', help='draw 1sigma and 2sigma contour line?')
argParser.add_argument('--smooth',             action='store_true', help='smooth histogram?')
argParser.add_argument('--level',              action='store',     default='reco', nargs='?', choices=['reco', 'gen'], help='Which level of reconstruction? reco, gen')
argParser.add_argument('--variables' ,         action='store',     default = ['ctZ', 'ctZI'], type=str, nargs=2, help = "argument plotting variables")
argParser.add_argument('--binning',            action='store',     default = [1, -2, 2, 1, -2, 2], type=float, nargs=6, help = "argument parameters")
argParser.add_argument('--zRange',             action='store',     default = [None, None], type=float, nargs=2, help = "argument parameters")
argParser.add_argument('--luminosity',         action='store',     default=150, type=int, help='Luminosity for weighting the plots')
argParser.add_argument('--scale',              action='store',     default=None, help='Luminosity for weighting the plots')
argParser.add_argument('--cores',              action='store',     default=8, type=int, help='number of cpu cores for multicore processing')
argParser.add_argument('--overwrite',          action='store_true', help='overwrite datafile?')
argParser.add_argument('--binMultiplier',      action='store',     default=3, type=int, help='bin multiplication factor')
argParser.add_argument('--detector',           action='store',     default='CMS', nargs='?', choices=['CMS', 'ATLAS', 'phase2_CMS'], help='Which Delphes detector simulation?')
argParser.add_argument('--scale14TeV',         action='store_true', help='scale 13 TeV cross-sections to 14 Tev?')
argParser.add_argument('--additionalCardFile', action='store',     default='TopEFTCardFile.txt', help='Cardfile where additional uncertainties are taken from')
argParser.add_argument('--addNonPrompt',       action='store_true', help='add nonPrompt?')
argParser.add_argument('--addUncertainties',   action='store',     default = ['trigger_2016','scale','scale_sig','PDF','PartonShower','nonprompt','WZ_xsec', 'ttX'], type=str, help = "add additional uncertainties from cardFile")
argParser.add_argument('--addBinNumberShift',  action='store',     default = 0, type=int, help = "which bin number does the region start in the additional card file?")
argParser.add_argument('--uncertaintyScale',   action='store',     default = 0.5, type=float, help = "scale factor for additional uncertainties")
argParser.add_argument('--statOnly',           action='store_true', help='use only statistical uncertainties')
argParser.add_argument('--noExpUnc',          action='store_true', help='use only statistical and theory uncertainties')

args = argParser.parse_args()

for unc in ['trigger_2016', 'nonprompt']:
    if unc in args.addUncertainties and args.noExpUnc: args.addUncertainties.remove(unc)

if args.level == 'gen':
    if 'ttZ' in args.process.split('_'):
        if args.small: from TTXPheno.Analysis.regions import genttZRegionsSmall as regions
        else:          from TTXPheno.Analysis.regions import genttZRegions as regions
    elif 'ttgamma' in args.process.split('_'):
        if args.small: from TTXPheno.Analysis.regions import genttgammaRegionsSmall as regions
        else:          from TTXPheno.Analysis.regions import genttgammaRegions as regions

    # Import additional functions/classes specified for the level of reconstruction
    from TTXPheno.Tools.cutInterpreterGen import cutInterpreter

elif args.level == 'reco':
    if 'ttZ' in args.process.split('_'):
        if args.small: from TTXPheno.Analysis.regions import recottZRegionsSmall as regions
        else:          from TTXPheno.Analysis.regions import recottZRegions as regions
    elif 'ttgamma' in args.process.split('_'):
        if args.small: from TTXPheno.Analysis.regions import recottgammaRegionsSmall as regions
        else:          from TTXPheno.Analysis.regions import recottgammaRegions as regions

    # Import additional functions/classes specified for the level of reconstruction
    from TTXPheno.Tools.cutInterpreterReco import cutInterpreter

#binning range
binningX = args.binning[:3]
binningY = args.binning[3:]

if binningX[0] > 1:
    xRange = np.linspace( binningX[1], binningX[2], int(binningX[0]), endpoint=False)
    xRange = [ el + 0.5 * ( xRange[1] - xRange[0] ) for el in xRange ]
else:
    xRange = [ 0.5 * ( binningX[1] + binningX[2] ) ]

if binningY[0] > 1:
    yRange = np.linspace( binningY[1], binningY[2], int(binningY[0]), endpoint=False)
    yRange = [ el + 0.5 * ( yRange[1] - yRange[0] ) for el in yRange ]
else:
    yRange = [ 0.5 * ( binningY[1] + binningY[2] ) ]

addon = []
if args.statOnly: addon += ["statOnly"]
if args.noExpUnc: addon += ["noExpUnc"]
#save data file
filename = '_'.join( ['nll', args.detector ] + args.sample.split('_')[1:3] + args.variables + map( str, args.binning ) + [ args.selection, str(args.luminosity), "14TeV" if args.scale14TeV else "13TeV" ] + addon ) + '.data'

#do the calculation
if not os.path.isfile('dat/' + filename) or args.overwrite:

    if not args.fitOnly:

        # Import samples
        sample_file     = "$CMSSW_BASE/python/TTXPheno/samples/benchmarks.py"
        loadedSamples   = imp.load_source( "samples", os.path.expandvars( sample_file ) )

        ttXSample       = getattr( loadedSamples, args.sample + '_%s' %args.detector )
        WZSample        = getattr( loadedSamples, 'fwlite_WZ_lep_LO_order2_15weights_%s' %args.detector )
    #    ttSample        = getattr( loadedSamples, 'fwlite_tt_full_LO_order2_15weights_%s' %args.detector )
    #    tWSample        = getattr( loadedSamples, 'fwlite_tW_LO_order2_15weights_%s' %args.detector )
        tWZSample       = getattr( loadedSamples, 'fwlite_tWZ_LO_order2_15weights_%s' %args.detector )
        tZqSample       = getattr( loadedSamples, 'fwlite_tZq_LO_order2_15weights_%s' %args.detector )
    #    ZgammaSample    = getattr( loadedSamples, 'fwlite_Zgamma_LO_order2_15weights_%s' %args.detector )
        ttgammaSample   = getattr( loadedSamples, 'fwlite_ttgamma_bg_LO_order2_15weights_%s' %args.detector )

        #if args.process.split('_')[0] == 'ttgamma':
        #    ttgammaIsrSample  = copy.deepcopy( ttXSample ) #select ttgamma events with isolated gamma from ISR (cat a2)
        #    ttgammaIsrSample.name = 'fwlite_ttgamma_ISR_LO_order2_15weights_ref'

        if args.process == 'ttZ_3l': bg = [ WZSample, tWZSample, tZqSample, ttgammaSample ]
    #    if args.process == 'ttZ_3l': bg = [ WZSample, tWZSample, tZqSample ]
        elif args.process == 'ttZ_4l': bg = [ WZSample, tWZSample, tZqSample, ttgammaSample ]
        elif args.process == 'ttgamma_1l': bg = [ ttSample, tWSample, tWZSample, tZqSample, ZgammaSample ]
        elif args.process == 'ttgamma_2l': bg = [ ttSample, tWSample, tWZSample, tZqSample, ZgammaSample ]

        def checkReferencePoint( sample ):
            ''' check if sample is simulated with a reference point
            '''
            return pickle.load(file(sample.reweight_pkl))['ref_point'] != {}

        # set selection string
        selectionString      = cutInterpreter.cutString(args.selection)
        selectionString_up   = selectionString.replace('nBTag','nBTag_JEC_up').replace('nrecoJet','nrecoJets_JEC_up')
        selectionString_down = selectionString.replace('nBTag','nBTag_JEC_down').replace('nrecoJet','nrecoJets_JEC_down')

        # somehow has to be separate from the next loop
        if args.small:
            for s in [ttXSample] + bg:
                s.reduceFiles( to = 15 )

        # configure samples
        for s in [ttXSample] + bg:

            s.event_factor = s.nEvents / float( s.chain.GetEntries() )
            s.xsecScaleFactor = s.xsec14 / s.xsec if args.scale14TeV else 1.
            s.weightInfo = WeightInfo( s.reweight_pkl )
            s.weightInfo.set_order( args.order )
            s.setSelectionString( selectionString )

            if checkReferencePoint( s ):
                s.setWeightString( 'ref_lumiweight1fb*(%s)*(%s)*(%s)'%( str(args.luminosity), str(s.event_factor), str(s.xsecScaleFactor) ) )
            else:
                s.setWeightString( 'lumiweight1fb*(%s)*(%s)*(%s)'%( str(args.luminosity), str(s.event_factor), str(s.xsecScaleFactor) ) )

        # overlap removal
        if args.process.split('_')[0] == 'ttgamma':
            ttXSample.addSelectionString( "(nonIsoPhoton!=1)" )
            ttSample.addSelectionString(  "(nonIsoPhoton==1)" )

        for var in args.variables:
            if var not in ttXSample.weightInfo.variables and not (args.variables[0] == 'cuB' and args.variables[1] == 'cuW'):
                raise ValueError('Input variable not in gridpack: %s' %var)

        observation                    = {}

        signal_btagging_uncertainty    = {}
        signal_mistagging_uncertainty  = {}
        signal_jes_uncertainty         = {}
        signal_electronId_uncertainty  = {}
        signal_muonId_uncertainty      = {}

        ttX_SM_rate                  = {}
        ttX_coeffList                = {}

        ttX_coeffList_reweighted_btagging   = {}
        ttX_coeffList_reweighted_mistagging = {}
        ttX_coeffList_reweighted_muonId     = {}
        ttX_coeffList_reweighted_electronId = {}
        ttX_coeffList_reweighted_jes_up     = {}
        ttX_coeffList_reweighted_jes_down   = {}

        background_rate                   = {}
        background_btagging_uncertainty   = {}
        background_mistagging_uncertainty = {}
        background_jes_uncertainty        = {}
        background_electronId_uncertainty = {}
        background_muonId_uncertainty     = {}

        nonPromptObservation              = {}

        for i_region, region in enumerate(regions):
            # compute signal yield for this region (this is the final code)

            logger.info( "At region %s", region )

            # ttX SM
            ttX_coeffList[region]  = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString() )
            ttX_SM_rate[region]    = ttXSample.weightInfo.get_weight_yield( ttX_coeffList[region] )

            if not args.statOnly:
                # uncertainty coeffLists
                ttX_coeffList_reweighted_btagging[region]   = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString(), weightString="reweight_BTag_B" )
                ttX_coeffList_reweighted_mistagging[region] = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString(), weightString="reweight_BTag_L" )
                ttX_coeffList_reweighted_muonId[region]     = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString(), weightString="reweight_id_mu" )
                ttX_coeffList_reweighted_electronId[region] = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString(), weightString="reweight_id_ele" )

                ttXSample.setSelectionString( selectionString_up )
                ttX_coeffList_reweighted_jes_up[region]     = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString() )

                ttXSample.setSelectionString( selectionString_down )
                ttX_coeffList_reweighted_jes_down[region]   = ttXSample.weightInfo.getCoeffListFromDraw( ttXSample, selectionString = region.cutString() )

                # reset selectionstring
                ttXSample.setSelectionString( selectionString )

            background_rate[region]                   = {}
            background_btagging_uncertainty[region]   = {}
            background_mistagging_uncertainty[region] = {}
            background_jes_uncertainty[region]        = {}
            background_muonId_uncertainty[region]     = {}
            background_electronId_uncertainty[region] = {}

            for i_background, background in enumerate(bg):
                # compute bg yield for this region (this is the final code)

                background_rate                 [region][background.name] = background.getYieldFromDraw( selectionString=region.cutString() )['val']

                if not args.statOnly and not args.noExpUnc:
                    #calculate btagging uncert.
                    background_rate_reweighted                                = background.getYieldFromDraw( selectionString=region.cutString(), weightString="reweight_BTag_B" )['val']
                    background_btagging_uncertainty [region][background.name] = 1 + (( background_rate_reweighted - background_rate[region][background.name] ) / background_rate[region][background.name]) if background_rate[region][background.name] > 0 else 1.

                    #calculate mistagging uncert.
                    background_rate_reweighted                                = background.getYieldFromDraw( selectionString=region.cutString(), weightString="reweight_BTag_L" )['val']
                    background_mistagging_uncertainty [region][background.name] = 1 + (( background_rate_reweighted - background_rate[region][background.name] ) / background_rate[region][background.name]) if background_rate[region][background.name] > 0 else 1.

                    #calculate muon Id uncert.
                    background_rate_reweighted                                = background.getYieldFromDraw( selectionString=region.cutString(), weightString="reweight_id_mu" )['val']
                    background_muonId_uncertainty [region][background.name] = 1 + (( background_rate_reweighted - background_rate[region][background.name] ) / background_rate[region][background.name]) if background_rate[region][background.name] > 0 else 1.

                    background_rate_reweighted                                = background.getYieldFromDraw( selectionString=region.cutString(), weightString="reweight_id_ele" )['val']
                    #calculate electron Id uncert.
                    background_electronId_uncertainty [region][background.name] = 1 + (( background_rate_reweighted - background_rate[region][background.name] ) / background_rate[region][background.name]) if background_rate[region][background.name] > 0 else 1.

                    # set selectionstring to JES_up
                    background.setSelectionString( selectionString_up )
                    background_rate_reweighted_up                             = background.getYieldFromDraw( selectionString=region.cutString() )['val']
                    # set selectionstring to JES_up
                    background.setSelectionString( selectionString_down )
                    background_rate_reweighted_down                           = background.getYieldFromDraw( selectionString=region.cutString() )['val']
                    # reset selectionstring
                    background.setSelectionString( selectionString )
                    #calculate JES uncert.
                    background_jes_uncertainty      [region][background.name] = 1 + (( background_rate_reweighted_up - background_rate_reweighted_down ) / (2*background_rate[region][background.name])) if background_rate[region][background.name] > 0 else 1.

            nonPromptObservation[region] = 0.
            if args.addNonPrompt:
                # scale nonprompt observation value from Run2 to args.luminosity
                nonPromptObservation[region] = getObservationValue( args.additionalCardFile, args.addBinNumberShift + i_region, 'nonPromptDD' ) * float(args.luminosity) / 35.9

            # Our expected observation :-)
            # add nonPrompt observation to total observation
            observation[region] = int( round( sum( background_rate[region].values() ) + ttX_SM_rate[region] + nonPromptObservation[region] ) )

    # Write temporary card file
    from TTXPheno.Tools.cardFileWriter import cardFileWriter
#    c = cardFileWriter.cardFileWriter()
    if args.useCombine:
        from TTXPheno.Tools.user import combineReleaseLocation
#        c.releaseLocation = combineReleaseLocation
    else:
        # non CMS NLL plot
        from TTXPheno.Analysis.ProfiledLoglikelihoodFit import ProfiledLoglikelihoodFit

    def cuBWtoctWZ( cuB, cuW ):
        ''' transforms C_tZ and C_tW to C_uB and C_uW
            C_tZ = Re( -sW*C_uB + cW*C_uW )
            C_tW = Re( C_uW )
            arXiv: 1802.07237
        '''
        sW=0.4715
        cW=0.8819

        ctW = cuW
        ctZ = -sW*cuB + cW*cuW

        return ctZ, ctW

    def calculation( variables ):
    #def calculation( var1, var2 ):

        if args.variables[0] == 'cuB' and args.variables[1] == 'cuW':
            var1, var2 = variables #cuB cuW
            ctZ, ctW = cuBWtoctWZ( var1, var2 )
            kwargs = { 'ctZ':ctZ, 'ctW':ctW }
        else:
            var1, var2 = variables
            kwargs = { args.variables[0]:var1, args.variables[1]:var2 }

        nameList = args.sample.split('_')[1:3] + args.variables + args.binning + [ args.level, args.version, args.order, args.luminosity, "14TeV" if args.scale14TeV else "13TeV", args.selection, 'small' if args.small else 'full', 'statOnly' if args.statOnly else 'fullUnc' if not args.noExpUnc else 'noExpUnc', var1, var2 ]
        cardname = '%s_nll_card'%'_'.join( map( str, nameList ) )
        cardFilePath = os.path.join( cardfileLocation, cardname + '.txt' )

        c = cardFileWriter.cardFileWriter()
        if args.useCombine:
            c.releaseLocation = combineReleaseLocation

        if not args.fitOnly:
#            print 'run cardfile'

            # uncertainties
            c.reset()
            if not args.statOnly:
                if not args.noExpUnc:
                    c.addUncertainty('lumi',        'lnN')
                    c.addUncertainty('JES',         'lnN')
                    c.addUncertainty('btagging',    'lnN')
                    c.addUncertainty('mistagging',  'lnN')
                    c.addUncertainty('muonId',      'lnN')
                    c.addUncertainty('electronId',  'lnN')
                for unc in args.addUncertainties:
                    c.addUncertainty(unc,  'lnN')

            signal_rate                  = {}
            for i_region, region in enumerate(regions):

                signal_rate[region] = ttXSample.weightInfo.get_weight_yield( ttX_coeffList[region], **kwargs)

                if not args.statOnly and not args.noExpUnc:
                    # signal uncertainties
                    # btagging
                    signal_rate_reweighted   = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_btagging[region], **kwargs )
                    signal_btagging_uncertainty [region] = 1 + (( signal_rate_reweighted - signal_rate[region] ) / signal_rate[region]) if signal_rate[region] > 0 else 1.

                    # mistagging
                    signal_rate_reweighted   = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_mistagging[region], **kwargs )
                    signal_mistagging_uncertainty [region] = 1 + (( signal_rate_reweighted - signal_rate[region] ) / signal_rate[region]) if signal_rate[region] > 0 else 1.

                    # muonId
                    signal_rate_reweighted   = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_muonId[region], **kwargs )
                    signal_muonId_uncertainty [region] = 1 + (( signal_rate_reweighted - signal_rate[region] ) / signal_rate[region]) if signal_rate[region] > 0 else 1.

                    # electronId
                    signal_rate_reweighted   = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_electronId[region], **kwargs )
                    signal_electronId_uncertainty [region] = 1 + (( signal_rate_reweighted - signal_rate[region] ) / signal_rate[region]) if signal_rate[region] > 0 else 1.

                    # JES
                    signal_rate_reweighted_JES_up   = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_jes_up[region], **kwargs )
                    signal_rate_reweighted_JES_down = ttXSample.weightInfo.get_weight_yield( ttX_coeffList_reweighted_jes_down[region], **kwargs )
                    signal_jes_uncertainty[region] = 1 + (( signal_rate_reweighted_JES_up - signal_rate_reweighted_JES_down ) / (2*signal_rate[region])) if signal_rate[region] > 0 else 1.

                bin_name = "Region_%i" % i_region
                nice_name = region.__str__()
                c.addBin(bin_name, ['_'.join(s.name.split('_')[1:3]) for s in bg] + ['nonPrompt'] if args.addNonPrompt else ['_'.join(s.name.split('_')[1:3]) for s in bg], nice_name)

                c.specifyObservation( bin_name, observation[region] )

                c.specifyExpectation( bin_name, 'signal', signal_rate[region]                                 )

                if not args.statOnly:
                    if not args.noExpUnc:
                        c.specifyFlatUncertainty( 'lumi', 1.01 )
                        c.specifyUncertainty( 'JES',        bin_name, 'signal', signal_jes_uncertainty[region]        )
                        c.specifyUncertainty( 'btagging',   bin_name, 'signal', signal_btagging_uncertainty[region]   )
                        c.specifyUncertainty( 'mistagging', bin_name, 'signal', signal_mistagging_uncertainty[region] )
                        c.specifyUncertainty( 'muonId',     bin_name, 'signal', signal_muonId_uncertainty[region]     )
                        c.specifyUncertainty( 'electronId', bin_name, 'signal', signal_electronId_uncertainty[region] )

                    for unc in args.addUncertainties:
                        c.specifyUncertainty( unc,      bin_name, 'signal', 1+(getUncertaintyValue( args.additionalCardFile, args.addBinNumberShift + i_region, 'signal', unc )-1)*args.uncertaintyScale )

                if args.addNonPrompt:
                    # for nonpromt only nonpromt uncertainty is important
                    c.specifyExpectation( bin_name, 'nonPrompt', nonPromptObservation[region] )
                    if not args.statOnly: c.specifyUncertainty( 'nonprompt',      bin_name, 'nonPrompt', 1+(getUncertaintyValue( args.additionalCardFile, args.addBinNumberShift + i_region, 'nonPromptDD', 'nonprompt' )-1)*args.uncertaintyScale )

                #c.specifyExpectation( bin_name, 'ttX_SM', ttX_SM_rate[region] )
                #c.specifyUncertainty( 'JES', bin_name, 'ttX_SM', ttX_SM_jes_uncertainty[region])
                #c.specifyUncertainty( 'btagging',bin_name, 'ttX_SM', ttX_SM_btagging_uncertainty[region])

                for background in bg:
                    c.specifyExpectation( bin_name, '_'.join( background.name.split('_')[1:3] ), background_rate[region][background.name] )
                    if not args.statOnly:
                        if not args.noExpUnc:
                            c.specifyUncertainty( 'JES',        bin_name, '_'.join( background.name.split('_')[1:3] ), background_jes_uncertainty[region][background.name])
                            c.specifyUncertainty( 'btagging',   bin_name, '_'.join( background.name.split('_')[1:3] ), background_btagging_uncertainty[region][background.name])
                            c.specifyUncertainty( 'mistagging', bin_name, '_'.join( background.name.split('_')[1:3] ), background_mistagging_uncertainty[region][background.name])
                            c.specifyUncertainty( 'muonId',     bin_name, '_'.join( background.name.split('_')[1:3] ), background_muonId_uncertainty[region][background.name])
                            c.specifyUncertainty( 'electronId', bin_name, '_'.join( background.name.split('_')[1:3] ), background_electronId_uncertainty[region][background.name])
                        for unc in args.addUncertainties:
                            if 'tZq' in background.name.split('_') or 'ttgamma' in background.name.split('_') or 'tWZ' in background.name.split('_'): proc = 'TTX'
                            elif 'WZ' in background.name.split('_'): proc = 'WZ'
                            else: raise ValueError('Background not found: %s' %background.name)
                            c.specifyUncertainty( unc,      bin_name, '_'.join( background.name.split('_')[1:3] ), 1+(getUncertaintyValue( args.additionalCardFile, args.addBinNumberShift + i_region, proc, unc )-1)*args.uncertaintyScale )

            c.writeToFile( cardFilePath )

        else:
            logger.info( "Running only NLL Fit with given CardFile %s"%cardFilePath)

        if not os.path.isfile( cardFilePath ):
            raise ValueError('CardFiles not found! Run script without --fitOnly!')

        if args.useCombine:
            # use the official cms combine tool
#                c.calcNuisances( cardFilePath, bestFit=args.bestFit )
            nll = c.calcNLL( cardFilePath, bestFit=args.bestFit )
#            nll = nll['nll0'] #pre-fit
            nll = nll['nll_abs'] #post-fit

            if args.removeCardFiles:
                for file in os.listdir( cardfileLocation ):
                    if file.startswith( cardname ):
                        os.remove( os.path.join( cardfileLocation, file ) )

        else:
            if args.bestFit: r = (0.99, 1.01)
            else: r = (0., 2.)

            profiledLoglikelihoodFit = ProfiledLoglikelihoodFit( cardFilePath )
            profiledLoglikelihoodFit.make_workspace(rmin=r[0], rmax=r[1])
            nll = profiledLoglikelihoodFit.likelihoodTest()
            profiledLoglikelihoodFit.cleanup(removeFiles=args.removeCardFiles)
            del profiledLoglikelihoodFit

        logger.info( "NLL: %f", nll)
        ROOT.gDirectory.Clear()

        # in very large WC regions, the fit fails, not relevant for the interesting regions
        if nll is None or abs(nll) > 10000 or abs(nll) < 1: nll = 999

        del c

        return var1, var2, nll

    results = []

    SM = calculation( (0, 0) )

    for varX in xRange:
        # do not run all calc in one pool, memory leak!!!
        pool = Pool( processes = args.cores )
        results += pool.map( calculation, [ (varX, varY) for varY in yRange ] )
        pool.close()
        del pool

    with open('tmp/'+filename, 'w') as f:
        for item in [SM]+results:
            f.write( "%s\n" % ','.join( map( str, list(item) ) ) )

else:
    with open('dat/'+filename, 'r') as f:
        data = f.readlines()

    results = []
    for i, line in enumerate(data):
        vals = map( float, line.split('\n')[0].split(',') )
        if args.scale is not None: vals[2] = vals[2]*float(args.scale)/float(args.luminosity)/2
        if i == 0:
            if vals[0] != 0 or vals[1] != 0:
                raise ValueError('SM Point in data file is not valid!')
            SM = tuple( vals )
        else: results.append( tuple( vals ) )

#Plot

#scale to SM
results.sort( key = lambda res: ( abs(res[0]), abs(res[1]), res[2] ) )
nll_SM = SM[2]

results = [ (x, y, 2*(result - nll_SM)) for x, y, result in results ]

def toGraph2D( name, title, data ):
    result = ROOT.TGraph2D( len(data) )
    debug = ROOT.TGraph()
    result.SetName( name )
    result.SetTitle( title )
    for i, datapoint in enumerate(data):
        x, y, val = datapoint
        result.SetPoint(i, x, y, val)
        debug.SetPoint(i, x, y)
    c = ROOT.TCanvas()
    result.Draw()
    debug.Draw()
    del c
    #res = ROOT.TGraphDelaunay(result)
    return result, debug

def convDipolesAnomalousCoupling( c2V, c2A ):
    ctZ  = c2V / 0.103
    ctZI = c2A / 0.103
    return round(ctZ,6), round(ctZI,6)

def convVectorAnomalousCoupling( c1V, c1A ):
    cpQM = ( c1V - c1A - 0.849 ) / ( -0.073 )
    cpt  = ( c1V + c1A + 0.357 ) / ( -0.073 )
    return round(cpQM,6), round(cpt,6)

def convVectorCoupling( cpQM, cpt ):
    c1V =  0.244 - 0.0365 * (cpQM + cpt)
    c1A = -0.601 + 0.0365 * (cpQM - cpt)
    return c1V, c1A

def convDipoles( ctZ, ctZI ):
    c2V = 0.103 * ctZ
    c2A = 0.103 * ctZI
    return c2V, c2A

def convertToAnomalousCouplings( res ):
    if "cpQM" in args.variables and "cpt" in args.variables:
        conversion = convVectorCoupling
        reversed = args.variables[0] == "cpt"

    elif "ctZ"  in args.variables and "ctZI" in args.variables:
        conversion = convDipoles
        reversed = args.variables[0] == "ctZI"

    else:
        raise ValueError('Anomalous Coupling conversion not implemented for %s'% " and ".join(args.variables))

    res_conv = []
    for x, y, nll in res:
        if reversed:
            y_conv, x_conv = conversion( y, x )
        else:
            x_conv, y_conv = conversion( x, y )

        res_conv.append( (x_conv, y_conv, nll) )

    return res_conv

#get TGraph2D from results list
a, debug = toGraph2D( args.process, args.process, results )#res_dic)
resultsAC = convertToAnomalousCouplings(results)
allX = [x for x, y, nll in resultsAC ]
allY = [y for x, y, nll in resultsAC ]
resultsAC.append( ( min(allX),min(allY),999) )
resultsAC.append( ( min(allX),max(allY),999) )
resultsAC.append( ( max(allX),min(allY),999) )
resultsAC.append( ( max(allX),max(allY),999) )
if "cpQM" in args.variables and "cpt" in args.variables:
    resultsAC.append( ( -0.47, -0.53, 32) )
    resultsAC.append( ( -0.48, -0.55, 32) )
    resultsAC.append( ( -0.48, -0.58, 32) )
    resultsAC.append( (  0.61, -0.58, 32) )
    resultsAC.append( (  0.61, -0.52, 32) )
    resultsAC.append( (  0.58, -0.5, 32) )

ac, debug = toGraph2D( args.process + "ac", args.process + "ac", resultsAC )#res_dic)
nxbins   = max(1, min(500, int(binningX[0])*int(args.binMultiplier)))
nybins   = max(1, min(500, int(binningY[0])*int(args.binMultiplier)))

#re-bin
hist = a.GetHistogram().Clone()
a.SetNpx(nxbins)
a.SetNpy(nybins)
hist = a.GetHistogram().Clone()

histac = ac.GetHistogram().Clone()
ac.SetNpx(nxbins)
ac.SetNpy(nybins)
histac = ac.GetHistogram().Clone()

#smoothing
if args.smooth:
    hist.Smooth()
    histac.Smooth()

cans = ROOT.TCanvas("can_%s"%args.process,"",500,500)

#calculate contour lines (1sigma, 2sigma) for 2D
contours = {'ttZ_3l': [2.28, 5.99]}
if args.contours:
    histsForCont = hist.Clone()
    c_contlist = ((ctypes.c_double)*(len(contours[args.process])))(*contours[args.process])
    histsForCont.SetContour(len(c_contlist),c_contlist)
    histsForCont.Draw("contzlist")
    cans.Update()
    conts = ROOT.gROOT.GetListOfSpecials().FindObject("contours")
    #cont_m2 = conts.At(0).Clone()
    #cont_m1 = conts.At(1).Clone()
    cont_p1 = conts.At(0).Clone()
    cont_p2 = conts.At(1).Clone()

pads = ROOT.TPad("pad_%s"%args.process,"",0.,0.,1.,1.)
pads.SetRightMargin(0.20)
pads.SetLeftMargin(0.14)
pads.SetTopMargin(0.11)
pads.Draw()
pads.cd()

hist.Draw("colz")

#draw contour lines
if args.contours:
    for conts in [cont_p2]:
        for cont in conts:
            cont.SetLineColor(ROOT.kOrange+7)
            cont.SetLineWidth(3)
#            cont.SetLineStyle(7)
            cont.Draw("same")
    for conts in [cont_p1]:
        for cont in conts:
            cont.SetLineColor(ROOT.kSpring-1)
            cont.SetLineWidth(3)
#            cont.SetLineStyle(7)
            cont.Draw("same")

hist.GetZaxis().SetTitle("-2 #Delta ln L")

if not None in args.zRange:
    hist.GetZaxis().SetRangeUser( args.zRange[0], args.zRange[1] )
#    hist.GetXaxis().SetRangeUser( -0.3 , 0.3 )
#    hist.GetYaxis().SetRangeUser( -0.3 , 0.3 )
#    hist.GetXaxis().SetRangeUser( -1 , 1 )
#    hist.GetYaxis().SetRangeUser( -1 , 1 )
#    hist.GetXaxis().SetRangeUser( -8 , 12 )
#    hist.GetYaxis().SetRangeUser( -8 , 12 )

if args.variables[0] == 'cuB' and args.variables[1] == 'cuW':
    hist.GetXaxis().SetTitle('C^{(33)}_{uB} (#Lambda/TeV)^{2}' )
    hist.GetYaxis().SetTitle('C^{(33)}_{uW} (#Lambda/TeV)^{2}' )
else:
    xTitle = args.variables[0].replace('c','C_{').replace('p','#phi').replace('M','') + '}'
    if 'I' in xTitle: xTitle = xTitle.replace('I','') + '^{[Im]}'
    yTitle = args.variables[1].replace('c','C_{').replace('p','#phi').replace('M','') + '}'
    if 'I' in yTitle: yTitle = yTitle.replace('I','') + '^{[Im]}'
    hist.GetXaxis().SetTitle( xTitle + ' (#Lambda/TeV)^{2}' )
    hist.GetYaxis().SetTitle( yTitle + ' (#Lambda/TeV)^{2}' )

hist.GetXaxis().SetTitleFont(42)
hist.GetYaxis().SetTitleFont(42)
hist.GetZaxis().SetTitleFont(42)
hist.GetXaxis().SetLabelFont(42)
hist.GetYaxis().SetLabelFont(42)
hist.GetZaxis().SetLabelFont(42)

hist.GetXaxis().SetTitleOffset(1.15)
hist.GetYaxis().SetTitleOffset(1.25)

hist.GetXaxis().SetTitleSize(0.045)
hist.GetYaxis().SetTitleSize(0.045)
hist.GetZaxis().SetTitleSize(0.042)
hist.GetXaxis().SetLabelSize(0.04)
hist.GetYaxis().SetLabelSize(0.04)
hist.GetZaxis().SetLabelSize(0.04)

latex1 = ROOT.TLatex()
latex1.SetNDC()
latex1.SetTextSize(0.04)
latex1.SetTextFont(42)
latex1.SetTextAlign(11)

latex1.DrawLatex(0.03, 0.92, '#bf{CMS Phase-2} #it{Simulation Preliminary}'),
#latex1.DrawLatex(0.15, 0.95, '#bf{CMS Phase-2} #it{Simulation Preliminary}'),
latex1.DrawLatex(0.68, 0.92, '%i ab{}^{-1} (%s TeV)' % (int(args.luminosity/1000.), "14" if args.scale14TeV else "13"))

latex2 = ROOT.TLatex()
latex2.SetNDC()
latex2.SetTextSize(0.04)
latex2.SetTextFont(42)
latex2.SetTextAlign(11)

#latex2.DrawLatex(0.15, 0.9, 'with Stat. uncert. only' if args.statOnly else 'with YR18 syst. uncert.' if not args.noExpUnc else 'w/o Exp. uncert.'),
if (args.noExpUnc or args.statOnly): latex2.DrawLatex(0.17, 0.82, 'Stat. uncert. only' if args.statOnly else 'w/o Exp. uncert.'),

#latex1.DrawLatex(0.15, 0.92, ' '.join(args.process.split('_')[:2]) + ' (' + args.detector + ')')
#latex1.DrawLatex(0.55, 0.92, '%3.1f fb{}^{-1} @ 13 TeV'%(float(args.luminosity) if args.scale is None else float(args.scale)) )

plot_directory_ = os.path.join(\
    plot_directory,
    '%s_%s'%(args.level, args.version),
    args.detector,
    args.sample,
    'backgrounds',
    'nll_small' if args.small else 'nll',
    args.selection)

if not os.path.isdir( plot_directory_ ):
    os.makedirs( plot_directory_ )

for e in [".png",".pdf",".root"]:
    cans.Print( plot_directory_ + '/' + '_'.join(args.variables + ['lumi'+str(args.luminosity) if args.scale is None else 'lumi'+str(args.scale), "14TeV" if args.scale14TeV else "13TeV", "CMScombine" if args.useCombine else "privateFit", "bestFit" if args.bestFit else "r1", 'statOnly' if args.statOnly else 'fullUnc' if not args.noExpUnc else 'noExpUnc']) + e)

del cans
del hist
# plot anomalous couplings

args.variables[0] = args.variables[0].replace('pQM','1V').replace('pt','1A').replace('tZ','2V').replace('2VI','2A')
args.variables[1] = args.variables[1].replace('pQM','1V').replace('pt','1A').replace('tZ','2V').replace('2VI','2A')

cans = ROOT.TCanvas("can_%s"%args.process,"",500,500)

#calculate contour lines (1sigma, 2sigma) for 2D
contours = {'ttZ_3l': [2.28, 5.99]}
if args.contours:
    histsForCont = histac.Clone()
    c_contlist = ((ctypes.c_double)*(len(contours[args.process])))(*contours[args.process])
    histsForCont.SetContour(len(c_contlist),c_contlist)
    histsForCont.Draw("contzlist")
    cans.Update()
    conts = ROOT.gROOT.GetListOfSpecials().FindObject("contours")
    #cont_m2 = conts.At(0).Clone()
    #cont_m1 = conts.At(1).Clone()
    cont_p1 = conts.At(0).Clone()
    cont_p2 = conts.At(1).Clone()

pads = ROOT.TPad("pad_%s"%args.process,"",0.,0.,1.,1.)
pads.SetRightMargin(0.20)
pads.SetLeftMargin(0.14)
pads.SetTopMargin(0.11)
pads.Draw()
pads.cd()

histac.Draw("colz")

#draw contour lines
if args.contours:
    for conts in [cont_p2]:
        for cont in conts:
            cont.SetLineColor(ROOT.kOrange+7)
            cont.SetLineWidth(3)
#            cont.SetLineStyle(7)
            cont.Draw("same")
    for conts in [cont_p1]:
        for cont in conts:
            cont.SetLineColor(ROOT.kSpring-1)
            cont.SetLineWidth(3)
#            cont.SetLineStyle(7)
            cont.Draw("same")

histac.GetZaxis().SetTitle("-2 #Delta ln L")

if not None in args.zRange:
    histac.GetZaxis().SetRangeUser( args.zRange[0], args.zRange[1] )
#    if "c1V" in args.variables and "c1A" in args.variables:
#        histac.GetYaxis().SetRangeUser( -0.8 , -0.4 )
#    histac.GetXaxis().SetRangeUser( -1 , 1 )
#    histac.GetYaxis().SetRangeUser( -1 , 1 )
#    histac.GetXaxis().SetRangeUser( -8 , 12 )
#    histac.GetYaxis().SetRangeUser( -8 , 12 )

xTitle = args.variables[0].replace('c','C_{') + '}'
yTitle = args.variables[1].replace('c','C_{') + '}'

histac.GetXaxis().SetTitle( xTitle )
histac.GetYaxis().SetTitle( yTitle )

histac.GetXaxis().SetTitleFont(42)
histac.GetYaxis().SetTitleFont(42)
histac.GetZaxis().SetTitleFont(42)
histac.GetXaxis().SetLabelFont(42)
histac.GetYaxis().SetLabelFont(42)
histac.GetZaxis().SetLabelFont(42)

histac.GetXaxis().SetTitleOffset(1.15)
histac.GetYaxis().SetTitleOffset(1.25)

histac.GetXaxis().SetTitleSize(0.045)
histac.GetYaxis().SetTitleSize(0.045)
histac.GetZaxis().SetTitleSize(0.042)
histac.GetXaxis().SetLabelSize(0.04)
histac.GetYaxis().SetLabelSize(0.04)
histac.GetZaxis().SetLabelSize(0.04)

latex1 = ROOT.TLatex()
latex1.SetNDC()
latex1.SetTextSize(0.04)
latex1.SetTextFont(42)
latex1.SetTextAlign(11)

latex1.DrawLatex(0.03, 0.92, '#bf{CMS Phase-2} #it{Simulation Preliminary}'),
#latex1.DrawLatex(0.15, 0.95, '#bf{CMS Phase-2} #it{Simulation Preliminary}'),
latex1.DrawLatex(0.68, 0.92, '%i ab{}^{-1} (%s TeV)' % (int(args.luminosity/1000.), "14" if args.scale14TeV else "13"))

latex2 = ROOT.TLatex()
latex2.SetNDC()
latex2.SetTextSize(0.04)
latex2.SetTextFont(42)
latex2.SetTextAlign(11)

#latex2.DrawLatex(0.15, 0.9, 'with Stat. uncert. only' if args.statOnly else 'with YR18 syst. uncert.' if not args.noExpUnc else 'w/o Exp. uncert.'),
if (args.noExpUnc or args.statOnly): latex2.DrawLatex(0.17, 0.82, 'Stat. uncert. only' if args.statOnly else 'w/o Exp. uncert.'),

#latex1.DrawLatex(0.15, 0.92, ' '.join(args.process.split('_')[:2]) + ' (' + args.detector + ')')
#latex1.DrawLatex(0.55, 0.92, '%3.1f fb{}^{-1} @ 13 TeV'%(float(args.luminosity) if args.scale is None else float(args.scale)) )

plot_directory_ = os.path.join(\
    plot_directory,
    '%s_%s'%(args.level, args.version),
    args.detector,
    args.sample,
    'backgrounds',
    'nll_small' if args.small else 'nll',
    args.selection)

if not os.path.isdir( plot_directory_ ):
    os.makedirs( plot_directory_ )

for e in [".png",".pdf",".root"]:
    cans.Print( plot_directory_ + '/' + '_'.join(args.variables + ['lumi'+str(args.luminosity) if args.scale is None else 'lumi'+str(args.scale), "14TeV" if args.scale14TeV else "13TeV", "CMScombine" if args.useCombine else "privateFit", "bestFit" if args.bestFit else "r1", 'statOnly' if args.statOnly else 'fullUnc' if not args.noExpUnc else 'noExpUnc']) + e)


import mxnet as mx
from autogluon.model_zoo import get_model

def test_image_classification_models():
    model_list = ['standford_dog_resnet152_v1', 'standford_dog_resnext101_64x4d',
                  'efficientnet_b0', 'efficientnet_b1', 'efficientnet_b2',
                  'efficientnet_b3', 'efficientnet_b4', 'efficientnet_b5',
                  'efficientnet_b6', 'efficientnet_b7']
    x = mx.nd.random.uniform(shape=(1, 3, 224, 224))
    for model_name in model_list:
        # get the model
        net = get_model(model_name, pretrained=True)
        # test inference
        y = net(x)

if __name__ == '__main__':
    test_image_classification_models()


'''
Created on Sep 18, 2014

@author: Seyed Iman Mirzaei
'''

#import numpy as np
import numpy as np
import scipy.optimize as opt
print('JJObject v1.0.0')

class junction():
    version='1.0.0'
    def __init__(self, Tc, T, Area, RnUnit,
                 CjUnit, Cground):

    #def __init__(self, Tc=1.23, T=20e-3, Area=4e-13, RnUnit=1300e-12,
    #            CjUnit=50e-3, Cground=30e-18):

        # Defining physical constants
        self.ec_ = 1.602176565e-19  # elementary charge (C)
        self.c_ = 299792458  # speed of light (m.s^-1)
        self.eps0_ = 8.854187817e-12  # electric constant (F.m^-1)
        self.mu0_ = 4*np.pi*1e-7  # magnetic constant (N.A^-2)
        self.h_ = 6.62606957e-34  # planck constant (J.s)
        self.hbar_ = self.h_/(2*np.pi)  # reduced planck constant (J.s)
        self.kb_ = 1.3806488e-23  # Boltzman coefficient (J.K^-1)
        self.Phi0_ = self.h_/(2*self.ec_)  # magnetic flux quanta (Wb)

        # Defining junction constants from parameters passed to the object
        self.Tc = Tc  # critical temperature in kelvin
        self.T = T  # junction temperature in kelvin
        self.Area = Area  # junction area (m^2)
        self.RnUnit = RnUnit  # normal state resistance of the junction per unit area (ohm)
        self.CjUnit = CjUnit  # junction capacitance per unit area (Farads)
        self.Cground = Cground

        # Calculating junction properties
        self.update()

    def update(self, Tc=None, T=None, Area=None, RnUnit=None, CjUnit=None, Cground=None):
        if not Tc is None :
            self.Tc = Tc
        if not T is None :
            self.T = T
        if not Area is None :
            self.Area = Area
        if not RnUnit is None :
            self.RnUnit = RnUnit
        if not CjUnit is None :
            self.CjUnit = CjUnit
        if not Cground is None :
            self.Cground = Cground

        self.Delta0 = self.calcDelta0(self.Tc)
        self.Delta = self.calcDelta(self.T, self.Tc, self.Delta0)
        self.Rn = self.calcRn(self.Area, self.RnUnit)
        self.Cj = self.calcCj(self.Area, self.CjUnit)
        self.Ic = self.calcIc(self.Rn,self.Delta,self.T)
        self.Ej = self.calcEj(self.Ic)
        self.Ec = self.calcEc(self.Cj)
        # in the following case, the junction current is assumed to be a thousand
        # times smaller than the critical current
        self.Lj = self.calcLj_lowCurr(self.Ic, self.Ic*1e-3)
        self.F0 = self.calcF0(self.Lj, self.Cj)

    def calcDelta0(self,Tc):
        ''' This function calculates the zero kelvin gap value based on BCS theory'''
        return 1.764*self.kb_*Tc  # gap value in Joules

    def calcDelta(self,T,Tc,d0):
        ''' This function calculates the finite-temperature gap based on a simplified equation that is more valid around 0 and Tc'''
        return d0*np.tanh((np.pi/1.764)*np.sqrt(0.95333*((Tc/T)-1)))  # gap value in Joules

    def calcRn(self,Area,RnUnit):
        return RnUnit/Area

    def calcCj(self,Area,CjUnit):
        return CjUnit*Area

    def calcIc(self,Rn,Delta,T):
        '''This function calculates the junction critical current based on Ambegaokar-Baratoff relation [Ambegaokar1963]
        '''
        return (np.pi*Delta/(2*self.ec_*Rn))*np.tanh(Delta/(2*self.kb_*T))

    def calcEj(self,Ic):
        return self.h_*Ic/(4*self.ec_*np.pi)

    def calcEc(self,Cj):
        return self.ec_**2/(2*Cj)

    def calcLj_lowCurr(self,Ic,I):
        return self.Phi0_/(2*np.pi*Ic*np.sqrt(1-(I/Ic)**2))

    def calcF0(self,Lj,Cj):
        return 1/(2*np.pi*np.sqrt(Lj*Cj))

    def junctionInfo(self,PrintInfo=True):
        self.update()
        txt = ('\n\nJunction Information: \n' +
              '\n=================================================================' +
              '\n* F0 = ' + '%0.2f' % (self.F0/1e9) + ' GHz   Plasma frequency ' +
              '\n* Delta('+str(self.T)+'K) = ' + '%0.2f' % (self.Delta/(self.h_*1e9)) + ' GHz   Gap value'
              '\n* ic(' + str(self.T) + 'K) = ' + '%0.3f' % (self.Ic*1e-6/self.Area) + ' uA/um^2   Critical current density'
              '\n* Ic(' + str(self.T) + 'K) = ' + '%0.2f' % (self.Ic*1e9) + ' nA  Critical current'
              '\n*----------------------------------------------------------------'
              '\n* Cj = ' + '%0.3f' % (self.Cj*1e15) + ' fF'
              '\n* Lj = ' + '%0.3f' % (self.Lj*1e9) + ' nH'
              '\n* Rjn = ' + '%0.3f' % (self.Rn/1e3) + ' kOhm'
              '\n*----------------------------------------------------------------'
              '\n* Ej = ' + '%0.3f' % (self.Ej/(self.h_*1e9)) + ' GHz'
              '\n* Ec = ' + '%0.3f' % (self.Ec/(self.h_*1e9)) + ' GHz'
              '\n* Ej/Ec = ' + '%0.3f' % (self.Ej/self.Ec) +
              '\n================================================================= \n\n')

        if PrintInfo is True:
            print(txt)

        return txt

class array():
    def __init__(self, junction, Cs, N):
        self.h_ = 6.62606957e-34  # planck constant (J.s)
        self.kb_ = 1.3806488e-23  # Boltzman coefficient (J.K^-1)

        self.j = junction
        self.Cs = Cs
        self.N = N

        self.update()

    def update(self, junction = None, N = None, Cs = None):

        if not junction is None :
            self.j = junction
        if not N is None :
            self.N = N
        if not Cs is None :
            self.Cs = Cs

        self.slipRate = self.calcSlipRate(self.j.Ej,self.j.Ec,self.N)
        self.unloadedModes = self.calcModesUnloaded(self.j.F0, np.arange(1,np.floor(self.N/2)), self.N, self.j.Cj, self.j.Cground)
        self.loadedModes = self.calcModesLoaded(self.j.F0, np.arange(1,np.floor(self.N/2)), self.N, self.j.Cj, self.j.Cground,self.Cs,self.j.Ec,self.j.Ej)
        self.thermalPop = self.calcThermalPop(self.slipRate, self.j.T)

    def calcSlipRate(self,Ej,Ec,N):
        return ((1/self.h_)*N*16
                *np.sqrt((Ej*Ec/np.pi))
                *(Ej/(2*Ec))**0.25
                *np.exp(-np.sqrt(8*Ej/Ec)))

    def calcModesUnloaded(self,f0,n,N,Cj,C0):
        return f0*np.sqrt((1-np.cos(np.pi*n/N))/(1-np.cos(np.pi*n/N)+C0/(2*Cj)))

    def calcModesLoaded(self,f0,n_vec,N,C_j,C_0,C_s,E_c,E_j):
        #eq_even = lambda omega_l: -1020.0*np.sqrt(2)*C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))) - np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
        #eq_odd = lambda omega_l: -np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1)))) + 0.000490196078431373*np.sqrt(2)/(C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))

        sol = np.array([])
        eps = 1e2

        n_vec = np.int_(n_vec)
        for n in n_vec:

            if n%2==0: # even numbers
                #eq = lambda w_l_num: eq_even_num(nn,N_num,C0_num,Cj_num,Cs_num,w0_num,Ec_num,Ej_num,w_l_num)
                eq = lambda omega_l: -1020.0*np.sqrt(2)*C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))) - np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
                sol = np.append(sol,opt.ridder(eq,self.unloadedModes[n-1]*(1-1/n)+eps,self.unloadedModes[n-1]-eps))
            else:
                #eq = lambda w_l_num: eq_odd_num(nn,N_num,C0_num,Cj_num,Cs_num,w0_num,Ec_num,Ej_num,w_l_num)
                eq = lambda omega_l: -np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1)))) + 0.000490196078431373*np.sqrt(2)/(C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
                if n==1:
                    sol = np.append(sol,opt.ridder(eq,eps,self.unloadedModes[n-1]-eps))
                else:
                    sol = np.append(sol,opt.ridder(eq,self.unloadedModes[n-1]*(1-2/n)+eps,self.unloadedModes[n-1]-eps))

        return sol

    def calcThermalPop(self,slipRate,T):
        return np.exp(-2*slipRate*self.h_/(self.kb_*T))/(1 + np.exp(-2*slipRate*self.h_/(self.kb_*T)))*100

    def arrayInfo(self,PrintInfo=True):
        self.update()
        txt =('\n\nArray Information: \n' +
              '\n=================================================================' +
              '\n* Cs = ' + '%0.3f' % (self.Cs*1e15) + 'fF' +
              '\n* Cj/C0 = ' + '%0.3f' % (self.j.Cj/self.j.Cground) +
              '\n* Lj_array = ' + '%0.3f' % (1e9*self.N*self.j.Lj) + ' nH' +
              '\n* Ej_array = ' + '%0.3f' % (1e-9*self.j.Ej/(self.h_*self.N)) + ' GHz' +
              '\n* First unloaded mode = ' + '%0.3f' % (1e-9*self.unloadedModes[0]) + ' GHz    (K=1)' +
              '\n* First loaded mode = ' + '%0.3f' % (1e-9*self.loadedModes[0]) + ' GHz    (K=1)' +
              '\n* Phase slipRate = ' + '%0.2e' % (self.slipRate) + ' Hz' +
              '\n* Qubit thermal population = ' + '%0.2f' % (self.thermalPop) + ' %' +
              '\n================================================================= \n\n'
              )
        if PrintInfo is True:
            print(txt)

        return txt


class State:
	def __init__(self, data, level, fval):
		self.data = data
		self.level = level
		self.fval = fval

	def gen_child_state(self):
		x, y = self.find(self.data, '_')
		move_list = [[x, y-1], [x+1, y], [x, y+1], [x-1, y]]
		children = []
		for i in move_list:
			child = self.move(self.data, x, y, i[0], i[1])
			if child is not None:
				child_state = State(child, self.level + 1, 0)
				children.append(child_state)
		return children

	def move(self, st, x1, y1, x2, y2):
		if x2 >= 0 and x2 < len(self.data) and y2 >= 0 and y2 < len(self.data):
			temp = []
			temp = self.copy(st)
			temp_val = temp[x2][y2]
			temp[x2][y2] = temp[x1][y1]
			temp[x1][y1] = temp_val
			return temp
		else:
			return None

	def copy(self, root):
		temp = []
		for i in root:
			t = []
			for j in i:
				t.append(j)
			temp.append(t)
		return temp

	def find(self, st, x):
		for i in range(0, len(self.data)):
			for j in range(0, len(self.data)):
				if st[i][j] == x:
					return i,j

class Board:
	def __init__(self):
		self.n = 3
		self.start = []
		self.goal = []

	def accept(self):
		p = []
		for i in range(0, self.n):
			temp = input().split(" ")
			p.append(temp)
		return p

	def generate(self):
		# print("Enter the start state matrix")
		self.start = self.accept()
		# self.start = [[1, 2, 3], ['_', 4, 6], [7, 5, 8]]
		# print("Enter the goal state matrix")
		self.goal = self.accept()
		# self.goal = [[1, 2, 3], [4, 5, 6], [7, 8, '_']]

	def run(self):
		self.generate()
		a = AStar()
		a.astar(self.start, self.goal)
		b = BFS()
		b.bfs(self.start, self.goal)

class AStar:
	def __init__(self):
		self.n = 3
		self.open = []
		self.closed = []

	def f(self, start, goal):
		return self.h(start.data, goal)+start.level

	def h(self, start, goal):
		temp = 0
		for i in range(0,self.n):
			for j in range(0,self.n):
				if start[i][j] != goal[i][j] and start[i][j] != '_':
					temp += 1
		return temp

	def astar(self, start, goal):
		print("astar: ")
		start = State(start, 0, 0)
		start.fval = self.f(start, goal)
		self.open.append(start)

		moves = 0
		while True:
			moves += 1
			cur = self.open[0]
			print("")
			print("  | ")
			print("  | ")
			print(" \\\'/ \n")
			for i in cur.data:
				for j in i:
					print(j,end=" ")
				print("")
			if(self.h(cur.data,goal) == 0):
				print("cost:", moves, "// generated states:", len(self.closed)+len(self.open))
				break
			for i in cur.gen_child_state():
				i.fval = self.f(i,goal)
				self.open.append(i)
			self.closed.append(cur)
			del self.open[0]

			""" sort the opne list based on f value """
			self.open.sort(key = lambda x:x.fval,reverse=False)

class BFS:
	def __init__(self):
		self.n = 3

	def bfs(self, start, goal):
		print("\nBFS: ")
		level = 0
		start = State(start, 0, 0)
		queue = []
		gen_state_list = []
		queue.append(start)
		gen_state_list.append(start.data)

		moves = 0
		while queue:
			moves += 1
			cur = queue.pop(0)
			print("")
			print("  | ")
			print("  | ")
			print(" \\\'/ \n")
			for i in cur.data:
				for j in i:
					print(j,end=" ")
				print("")

			if cur.data == goal:
				print("cost:", moves, "// generated states:", len(gen_state_list))
				break

			for i in cur.gen_child_state():
				if i.data not in gen_state_list:
					queue.append(i)
					gen_state_list.append(i.data)

def main():
	board = Board()
	board.run()

main()


# "Alphabet Rangoli" Challenge (Not Completed)

# 1. get input
N = int(input())
width = 4 * N - 3
alphalist = [chr(i) for i in range(97, 97+N)]
charnum = 0

# 2. print out
for i in range(1, 2*N):
    if i <= N: charnum += 1
    else: charnum -= 1
    thislist = list(reversed(alphalist[N-charnum:])) + alphalist[N-1+charnum:]
    content = '-'.join(thislist)
    print(content.center(width, '-'))


#Lecture 11/13/18
#Trees

class BinaryTree:

    #constructor
    def __init__(self, rootObj):
        self.key = rootObj
        self.leftChild = None
        self.rightChild = None

    def insertLeft(self, newNode):
        #if no left child, insert as left child
        if self.leftChild == None:
            self.leftChild = BinaryTree(newNode)
        #if node has left child, push existing left child to the left
        #and insert new left child
        else:
            t = BinaryTree(newNode)
            t.leftChild = self.leftChild
            self.leftChild = t

    def insertRight(self, newNode):
        #if no right child, insert as right child
        if self.rightChild == None:
            self.rightChild = BinaryTree(newNode)
        #if node has left child, push existing left child to the left
        #and insert new left child
        else:
            t = BinaryTree(newNode)
            t.rightChild = self.rightChild
            self.rightChild = t

    #function returns right of child of node
    def getRightChild(self):
        return self.rightChild

    #function returns left child of node
    def getLeftChild(self):
        return self.leftChild

    #function sets root value
    def setRootVal(self, obj):
        self.key = obj

    #function returns value of root
    def getRootVal(self):
        return self.key

def main():
    #create binary tree

    myTree = BinaryTree('A')
    myTree.insertLeft('X')
    myTree.insertLeft('Z')
    myTree.insertLeft('B')
    myTree.getLeftChild().getLeftChild().insertRight('M')
    myTree.insertRight('F')
    myTree.insertRight('C')

    print(myTree.getRootVal()) #A
    print(myTree.getLeftChild().getRootVal()) #B
    print(myTree.getLeftChild().getLeftChild().getRootVal()) #Z
    print(myTree.getLeftChild().getLeftChild().getLeftChild().getRootVal()) #X
    print(myTree.getLeftChild().getLeftChild().getRightChild().getRootVal()) #M
    print(myTree.getRightChild().getRootVal()) #C
    print(myTree.getRightChild().getRightChild().getRootVal()) #F

main()


from collections import OrderedDict
from os import makedirs
import os
from os.path import expanduser
from scipy.spatial.distance import squareform, pdist
import time
import uuid

from independent_jobs.engines.BatchClusterParameters import BatchClusterParameters
from independent_jobs.engines.SerialComputationEngine import SerialComputationEngine
from independent_jobs.engines.SlurmComputationEngine import SlurmComputationEngine
from independent_jobs.tools.FileSystem import FileSystem

from kmc.densities.banana import Banana, sample_banana, norm_of_emp_mean
import kmc.densities.banana
from kmc.densities.gaussian import IsotropicZeroMeanGaussian
from kmc.tools.Log import logger
from kmc.tools.convergence_stats import avg_ess, min_ess
import numpy as np
from scripts.experiments.mcmc.independent_job_classes.HMCJob import HMCJob
from scripts.experiments.mcmc.independent_job_classes.KMCRandomFeatsJob import KMCRandomFeatsJob
from scripts.experiments.mcmc.independent_job_classes.KameleonJob import KameleonJob
from scripts.experiments.mcmc.independent_job_classes.RWJob import RWJob
from scripts.experiments.mcmc.independent_job_classes.debug import plot_diagnosis

modulename = __file__.split(os.sep)[-1].split('.')[-2]
start_base = [0, -3.]

statistics = OrderedDict()
statistics['avg_quantile_error']=kmc.densities.banana.avg_quantile_error
statistics['avg_ess']=avg_ess
statistics['min_ess']=min_ess
statistics['norm_of_mean']=norm_of_emp_mean

def get_start(D):
    start = np.array(start_base + [0. ] * (D - 2))
    return start

def hmc_generator(D, target, num_warmup, thin_step, momentum_seed):
    # determined by pilot runs
    if D == 2:
        step_size_min = 0.8
        step_size_max = 1.5
    elif D==8:
        step_size_min = 0.6
        step_size_max = 1.3

    start = get_start(D)
    momentum = IsotropicZeroMeanGaussian(sigma=sigma_p, D=D)
    return HMCJob(target, momentum, num_iterations, start,
                  num_steps_min, num_steps_max, step_size_min, step_size_max, momentum_seed,
                  statistics=statistics, num_warmup=num_warmup, thin_step=thin_step)

def rw_generator_isotropic(D, target, num_warmup, thin_step):
    # tuned towards roughly 23% acceptance
    sigmas_proposal = {
              2: 4.4,
              8: 1.,
              16: 0.35,
              32: 0.05
              }
    start = get_start(D)
    return RWJob(target, num_iterations, start, sigmas_proposal[D],
                 statistics=statistics,
                 num_warmup=num_warmup, thin_step=thin_step)

def kmc_generator(N, D, target, num_warmup, thin_step, momentum_seed):
    if D == 2:
        step_size_min = 0.8
        step_size_max = 1.5
    elif D==8:
        step_size_min = 0.6
        step_size_max = 1.3

    start = get_start(D)
    momentum = IsotropicZeroMeanGaussian(sigma=sigma_p, D=D)

    # estimator parameters
    sigma = 0.46
    lmbda = 0.000001

    learn_parameters = True if N < 500 else False
    force_relearn_parameters = True if N < 500 else False

    # oracle samples
    Z = sample_banana(N, D, bananicity, V)
    job = KMCRandomFeatsJob(Z, N, sigma, lmbda,
                            target, momentum, num_iterations,
                            start, num_steps_min, num_steps_max,
                            step_size_min, step_size_max, momentum_seed, learn_parameters=learn_parameters,
                            force_relearn_parameters=force_relearn_parameters,
                            statistics=statistics, num_warmup=num_warmup, thin_step=thin_step)
    job.plot = False
    return job

def kameleon_generator(N, D, target, num_warmup, thin_step):
    # determined by pilot runs
    if N==2000 and D==8:
        nu2 = .5
        gamma2 = .1
    elif N==1500 and D==8:
        nu2 = .7
        gamma2 = .1
    elif N==1000 and D==8:
        nu2 = .8
        gamma2 = .1
    elif N==500 and D==8:
        nu2 = .8
        gamma2 = .5
    elif N==200 and D==8:
        nu2 = 1.
        gamma2 = 1.
    elif N==100 and D==8:
        nu2 = 2.
        gamma2 = 1.
    elif N==50 and D==8:
        nu2 = 2.
        gamma2 = 1.
    # oracle samples
    Z = sample_banana(N, D, bananicity, V)

    # median heuristic:
    dists=squareform(pdist(Z, 'sqeuclidean'))
    median_dist=np.median(dists[dists>0])
    sigma=0.5*median_dist

    start = get_start(D)
    job = KameleonJob(Z, sigma, nu2, gamma2, target, num_iterations, start,
                      statistics=statistics,
                            num_warmup=num_warmup, thin_step=thin_step)
    return job

if __name__ == "__main__":
    logger.setLevel(10)
    Ds = np.sort([8])[::-1]
    Ns = np.sort([50, 100, 200, 500, 1000, 1500, 2000])[::-1]

    print(Ns)
    print(Ds)
    assert np.min(Ds) >= 2
    num_repetitions = 1
    num_repetitions = 10

    # target
    bananicity = 0.03
    V = 100
    target = Banana(bananicity, V)

    # plain MCMC parameters
    num_warmup = 500
    thin_step = 1
    num_iterations = 2000 + num_warmup
    num_iterations = 100
    num_warmup = 0

    # hmc parameters
    num_steps_min = 10
    num_steps_max = 100
    sigma_p = 1.
    momentum_seed = np.random.randint(time.time())

    compute_local = False

    if not FileSystem.cmd_exists("sbatch") or compute_local:
        engine = SerialComputationEngine()

    else:
        johns_slurm_hack = "#SBATCH --partition=intel-ivy,wrkstn,compute"
        folder = os.sep + os.sep.join(["nfs", "data3", "ucabhst", modulename])
        batch_parameters = BatchClusterParameters(foldername=folder,
                                                  resubmit_on_timeout=False,
                                                  parameter_prefix=johns_slurm_hack)
        engine = SlurmComputationEngine(batch_parameters, check_interval=1,
                                        do_clean_up=True)
        engine.max_jobs_in_queue = 1000
        engine.store_fire_and_forget = True

    aggs_hmc_kmc = {}
    aggs_rw_kameleon = {}
    for N in Ns:
        for D in Ds:
            aggs_hmc_kmc[D] = []
            aggs_hmc_kmc[(N, D)] = []
            aggs_rw_kameleon[D] = []
            aggs_rw_kameleon[(N, D)] = []

    for i in range(num_repetitions):
        # same momentum for every D and N of every repetition
        momentum_seed += 1
        for D in Ds:
            job = hmc_generator(D, target, num_warmup, thin_step, momentum_seed)
            logger.info("Repetition %d/%d, %s" % (i + 1, num_repetitions, job.get_parameter_fname_suffix()))
            aggs_hmc_kmc[D] += [engine.submit_job(job)]
            job = rw_generator_isotropic(D, target, num_warmup, thin_step)
            logger.info("Repetition %d/%d, %s" % (i + 1, num_repetitions, job.get_parameter_fname_suffix()))
            aggs_rw_kameleon[D] += [engine.submit_job(job)]
            for N in Ns:
                job = kmc_generator(N, D, target, num_warmup, thin_step, momentum_seed)
                logger.info("Repetition %d/%d, %s" % (i + 1, num_repetitions, job.get_parameter_fname_suffix()))
                aggs_hmc_kmc[(N, D)] += [engine.submit_job(job)]
                job = kameleon_generator(N, D, target, num_warmup, thin_step)
                logger.info("Repetition %d/%d, %s" % (i + 1, num_repetitions, job.get_parameter_fname_suffix()))
                aggs_rw_kameleon[(N, D)] += [engine.submit_job(job)]

    engine.wait_for_all()

    if isinstance(engine, SerialComputationEngine):
        directory = expanduser("~") + os.sep + modulename
        try:
            makedirs(directory)
        except OSError:
            pass
        for D in Ds:
            for agg in aggs_hmc_kmc[D]:
                job_name = unicode(uuid.uuid4())
                agg.store_fire_and_forget_result(directory, job_name)

            for N in Ns:
                for agg in aggs_hmc_kmc[(N, D)]:
                    job_name = unicode(uuid.uuid4())
                    agg.store_fire_and_forget_result(directory, job_name)

            for agg in aggs_rw_kameleon[D]:
                job_name = unicode(uuid.uuid4())
                agg.store_fire_and_forget_result(directory, job_name)

            for N in Ns:
                for agg in aggs_rw_kameleon[(N, D)]:
                    job_name = unicode(uuid.uuid4())
                    agg.store_fire_and_forget_result(directory, job_name)

        # plot some diagnosis

        for D in Ds:
            for agg in aggs_hmc_kmc[D]:
                print(agg.__class__.__name__)
                plot_diagnosis(agg, D)

            for agg in aggs_rw_kameleon[D]:
                print(agg.__class__.__name__)
                plot_diagnosis(agg, D)

            for N in Ns:
                for agg in aggs_hmc_kmc[(N, D)]:
                    print(agg.__class__.__name__)
                    plot_diagnosis(agg, D)

                for agg in aggs_rw_kameleon[(N, D)]:
                    print(agg.__class__.__name__)
                    plot_diagnosis(agg, D)


class Listing(object):
    def __init__(self, url, listing_id, repost_of, title, date, boro, hood, price, bedrooms, cats_ok, dogs_ok, no_fee, size="",):
        self.url = url
        self.listing_id = listing_id
        self.repost_of = repost_of
        self.title = title
        self.date = date
        self.boro = boro
        self.hood = hood
        self.price = price
        self.size = size
        self.bedrooms = bedrooms
        self.cats_allowed = cats_ok
        self.dogs_allowed = dogs_ok
        self.no_fee = no_fee

    def __repr__(self):
        return {'url':self.url,'listing_id':self.listing_id,'repost_of':self.repost_of, 'title':self.title, \
            'date':self.date, 'boro':self.boro, 'neighborhood':self.hood, 'price':self.price, 'size':self.size, 'bedrooms':self.bedrooms, \
            'cats_allowed':self.cats_allowed, 'dogs_allowed':self.dogs_allowed, 'no_fee':self.no_fee}


import synapse.exc as s_exc

import synapse.lib.node as s_node

import synapse.tests.utils as s_t_utils
from synapse.tests.utils import alist

class NodeTest(s_t_utils.SynTest):

    async def test_pack(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:

            await core.addTagProp('score', ('int', {}), {})

            async with await core.snap() as snap:

                node = await snap.addNode(form, valu, props=props)
                await node.setTagProp('foo', 'score', 10)

                iden, info = node.pack()
                self.eq(iden, ('test:str', 'cool'))
                self.eq(info.get('tags'), {'foo': (None, None)})
                self.eq(info.get('tagprops'), {'foo': {'score': 10}})
                props = {k: v for (k, v) in info.get('props', {}).items() if not k.startswith('.')}
                self.eq(props, {'tick': 12345})

                iden, info = node.pack(dorepr=True)
                self.eq(iden, ('test:str', 'cool'))
                self.eq(info.get('tags'), {'foo': (None, None)})
                # TODO - Add repr support for tagprops
                props = {k: v for (k, v) in info.get('props', {}).items() if not k.startswith('.')}
                self.eq(props, {'tick': 12345})
                self.eq(info.get('repr'), None)
                reprs = {k: v for (k, v) in info.get('reprs', {}).items() if not k.startswith('.')}
                self.eq(reprs, {'tick': '1970/01/01 00:00:12.345'})

                # Set a property on the node which is extra model and pack it.
                # This situation can be encountered in a multi-layer situation
                # where one Cortex can have model knowledge and set props
                # that another Cortex (sitting on top of the first one) lifts
                # a node which has props the second cortex doens't know about.
                node.props['.newp'] = 1
                node.props['newp'] = (2, 3)
                iden, info = node.pack(dorepr=True)
                props, reprs = info.get('props'), info.get('reprs')
                self.eq(props.get('.newp'), 1)
                self.eq(props.get('newp'), (2, 3))

                # without model knowledge it is impossible to repr a value so it should
                # *not* be in the repr dict
                self.none(reprs.get('newp'))
                self.none(reprs.get('.newp'))

    async def test_set(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                self.true(snap.strict)  # Following assertions based on snap.strict being true
                node = await snap.addNode(form, valu, props=props)

                self.false(await node.set('tick', 12345))
                self.true(await node.set('tick', 123456))
                await self.asyncraises(s_exc.NoSuchProp, node.set('notreal', 12345))

                ronode = await snap.addNode('test:comp', (1, 's'))
                await self.asyncraises(s_exc.ReadOnlyProp, ronode.set('hehe', 2))
                snap.strict = False
                self.false(await ronode.set('hehe', 3))
                snap.strict = True

    async def test_has(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                node = await snap.addNode(form, valu, props=props)

                self.true(node.has('tick'))
                self.true(node.has('.created'))
                self.false(node.has('nope'))
                self.false(node.has('.nope'))

    async def test_get(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                node = await snap.addNode(form, valu, props=props)
                await node.addTag('cool', valu=(1, 2))

                self.eq(node.get('tick'), 12345)
                self.none(node.get('nope'))

                self.eq(node.get('#cool'), (1, 2))
                self.none(node.get('#newp'))

    async def test_pop(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                node = await snap.addNode(form, valu, props=props)
                await node.addTag('cool', valu=(1, 2))

                await self.asyncraises(s_exc.NoSuchProp, node.pop('nope'))
                snap.strict = False
                self.false(await node.pop('nope'))
                snap.strict = True

                ronode = await snap.addNode('test:comp', (1, 's'))
                await self.asyncraises(s_exc.ReadOnlyProp, ronode.pop('hehe'))
                snap.strict = False
                self.false(await ronode.pop('hehe'))
                snap.strict = True

    async def test_repr(self):
        async with self.getTestCore() as core:
            async with await core.snap() as snap:

                form = 'test:str'
                valu = 'cool'
                props = {'tick': 12345}
                node = await snap.addNode(form, valu, props=props)
                self.eq('cool', node.repr())
                self.eq(node.repr('tick'), '1970/01/01 00:00:12.345')

                form = 'test:threetype'
                valu = 'cool'
                node = await snap.addNode(form, valu)
                self.eq(node.repr(), '3')
                reprs = {k: v for (k, v) in node.reprs().items() if not k.startswith('.')}
                self.eq(reprs.get('three'), '3')

    async def test_tags(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345}

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                self.true(snap.strict)

                node = await snap.addNode(form, valu, props=props)

                # Add a tag
                await node.addTag('cool', valu=(1, 2))
                self.eq(node.getTag('cool'), (1, 2))
                await node.addTag('cool', valu=(1, 2))  # Add again
                self.eq(node.getTag('cool'), (1, 2))
                await node.addTag('cool', valu=(1, 3))  # Add again with different valu
                self.eq(node.getTag('cool'), (1, 3))
                await node.addTag('cool', valu=(-5, 0))  # Add again with different valu
                self.eq(node.getTag('cool'), (-5, 3)) # merges...

                self.true(node.hasTag('cool'))
                self.true(node.hasTag('#cool'))
                self.false(node.hasTag('notcool'))
                self.false(node.hasTag('#notcool'))

                # Demonstrate that valu is only applied at the level that addTag is called
                await node.addTag('cool.beans.abc', valu=(1, 8))
                self.eq(node.getTag('cool.beans.abc'), (1, 8))
                self.eq(node.getTag('cool.beans'), (None, None))

                await self.asyncraises(s_exc.NoSuchProp, node.pop('nope'))
                snap.strict = False
                self.false(await node.pop('nope'))
                snap.strict = True

    async def test_helpers(self):
        form = 'test:str'
        valu = 'cool'
        props = {'tick': 12345,
                 'hehe': 'hehe',
                 }
        tval = (None, None)

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                node = await snap.addNode(form, valu, props=props)
                await node.addTag('test.foo.bar.duck', tval)
                await node.addTag('test.foo.baz', tval)
                await node.addTag('test.foo.time', ('2016', '2019'))
                await node.addTag('test.foo', ('2015', '2017'))
                pode = node.pack(dorepr=True)

                node2 = await snap.addNode('test:int', '1234')
                pode2 = node2.pack(dorepr=True)

        self.eq(s_node.ndef(pode), ('test:str', 'cool'))
        self.eq(s_node.reprNdef(pode), ('test:str', 'cool'))
        self.eq(s_node.ndef(pode2), ('test:int', 1234))
        self.eq(s_node.reprNdef(pode2), ('test:int', '1234'))

        e = 'bf1198c5f28dae61d595434b0788dd6f7206b1e62d06b0798e012685f1abc85d'
        self.eq(s_node.iden(pode), e)

        self.true(s_node.tagged(pode, 'test'))
        self.true(s_node.tagged(pode, '#test.foo.bar'))
        self.true(s_node.tagged(pode, 'test.foo.bar.duck'))
        self.false(s_node.tagged(pode, 'test.foo.bar.newp'))

        self.len(3, s_node.tags(pode, leaf=True))
        self.len(4, s_node.tagsnice(pode))
        self.len(6, s_node.tags(pode))
        self.eq(s_node.reprTag(pode, '#test.foo.bar'), '')
        self.eq(s_node.reprTag(pode, '#test.foo.time'), '(2016/01/01 00:00:00.000, 2019/01/01 00:00:00.000)')
        self.none(s_node.reprTag(pode, 'test.foo.newp'))

        self.eq(s_node.prop(pode, 'hehe'), 'hehe')
        self.eq(s_node.prop(pode, 'tick'), 12345)
        self.eq(s_node.prop(pode, ':tick'), 12345)
        self.eq(s_node.prop(pode, 'test:str:tick'), 12345)
        self.none(s_node.prop(pode, 'newp'))

        self.eq(s_node.reprProp(pode, 'hehe'), 'hehe')
        self.eq(s_node.reprProp(pode, 'tick'), '1970/01/01 00:00:12.345')
        self.eq(s_node.reprProp(pode, ':tick'), '1970/01/01 00:00:12.345')
        self.eq(s_node.reprProp(pode, 'test:str:tick'), '1970/01/01 00:00:12.345')
        self.none(s_node.reprProp(pode, 'newp'))

        props = s_node.props(pode)
        self.isin('.created', props)
        self.isin('tick', props)
        self.notin('newp', props)

    async def test_storm(self):

        async with self.getTestCore() as core:
            async with await core.snap() as snap:
                node = await snap.addNode('test:comp', (42, 'lol'))
                nodepaths = await alist(node.storm('-> test:int'))
                self.len(1, nodepaths)
                self.eq(nodepaths[0][0].ndef, ('test:int', 42))

                nodepaths = await alist(node.storm('-> test:int [:loc=$foo]', opts={'vars': {'foo': 'us'}}))
                self.eq(nodepaths[0][0].props.get('loc'), 'us')

                path = nodepaths[0][1].fork(node)
                path.vars['zed'] = 'ca'

                # Path present, opts not present
                nodes = await alist(node.storm('-> test:int [:loc=$zed] $bar=$foo', path=path))
                self.eq(nodes[0][0].props.get('loc'), 'ca')
                self.eq(path.vars.get('bar'), 'us')

                # Path present, opts present but no opts['vars']
                nodes = await alist(node.storm('-> test:int [:loc=$zed] $bar=$foo', opts={}, path=path))
                self.eq(nodes[0][0].props.get('loc'), 'ca')
                self.eq(path.vars.get('bar'), 'us')

                # Path present, opts present with vars
                nodes = await alist(node.storm('-> test:int [:loc=$zed] $bar=$baz',
                                               opts={'vars': {'baz': 'ru'}},
                                               path=path))
                self.eq(nodes[0][0].props.get('loc'), 'ca')
                self.eq(path.vars.get('bar'), 'ru')

    async def test_node_repr(self):

        async with self.getTestCore() as core:

            nodes = await core.nodes('[ inet:ipv4=1.2.3.4 :loc=us ]')
            self.len(1, nodes)

            node = nodes[0]

            self.eq('1.2.3.4', nodes[0].repr())

            self.eq('us', node.repr('loc'))

            with self.raises(s_exc.NoSuchProp):
                node.repr('newp')

            with self.raises(s_exc.NoPropValu):
                node.repr('dns:rev')


import tkinter as tk
import random

Questions = [
    "25% of 76",                    #MPC
    "18% of 200",                   #MPC
    "38% of 6200",                  #MPC
    "33.33% of $1200",              #MPC
    "1.25% of $3275",               #MPC
    "75% of $490",                  #MPC
    "15% of $1.60",                 #MPC
    "8% of $2",                     #MPC
    "66.66% of 930g",               #MPC
    "15% of 180",                   #MPC
    "is 118% of 450 = 531",         #TORF
    "is 115% of $40 = $50",         #TORF
    "is 200% of 450 = 900 ",        #TORF
    "is 108% of $13.50 = $16.58",   #TORF
    "is 72/480 in % = 15% ",        #TORF
    "is 13000 - 15% = 11050",       #TORF
    "is 425 - 4% = 400",            #TORF
    "is 450 - 85% = 65.5",          #TORF
    "is 24/20 in % = 120% ",        #TORF
    "is 13000/10400 in % = 130%"    #TORF
]
ButtonsText = [
    [
        "18", "19", "25"  #19
    ],
    [
        "36", "130", "30"  #36
    ],
    [
        "4329", "2300","2356"  #2356
    ],
    [
        "$380", "$500","$400"  #$400
    ],
    [
         "$41", "$40.94", "$40"  #$40.94
    ],
    [
        "$367.50", "$350", "$400"  #367.50
    ],
    [
        "24 cents", "50 cents", "$24"  #24 cents
    ],
    [
         "20 cents", "10", "16 cents"  #16 cents
    ],
    [
         "550g", "700g", "620g"  #620g
    ],
    [
         "30", "27", "50"  #27
    ],
    [
        "True", "False"  #True
    ],
    [
        "True", "False"  #false
    ],
    [
        "True", "False"  #True
    ],
    [
        "True", "False"  #False
    ],
    [
        "True", "False"  #True
    ],
    [
        "True", "False"  #True
    ],
    [
        "True", "False"  #False
    ],
    [
        "True", "False"  #False
    ],
    [
        "True", "False"  #True
    ],
    [
        "True", "False"  #False
    ],
]

Answers = [
    "b",
    "a",
    "a",
    "c",
    "b",
    "a",
    "a",
    "c",
    "c",
    "b",
    "true",
    "false",
    "true",
    "false",
    "true",
    "true",
    "false",
    "false",
    "true",
    "false"
]

Shuffle1 = list(zip(Questions, Answers, ButtonsText))   #Creates a
random.shuffle(Shuffle1)
Questions, Answers, ButtonsText = zip(*Shuffle1)

Correct = 0
skips = 0
i = 1

class StartScreen():
    def __init__(self):
        global Questions
        global Answers
        global ButtonsText
        global i
        global Correct
        global skips
        Correct = 0
        skips = 0
        i = 1
        ButtonPressed = False
        Shuffle1 = list(zip(Questions, Answers, ButtonsText))
        random.shuffle(Shuffle1)
        Questions, Answers, ButtonsText = zip(*Shuffle1)

        self.root = tk.Tk()
        self.root.geometry('800x500')
        self.root.configure(bg='#cfe2f3')
        self.root.title("Percentages Math Quiz")
        Title = tk.Label(self.root, text="PERCENTAGES MATH QUIZ", bg="#cceae3", font="Arial 18",width=28,height=3 ,borderwidth=1, relief="solid").place(x=225, y=80)
        Quit = tk.Button(self.root,
                           text = 'QUIT', borderwidth=1, relief="solid", bg="#cceae3", width=15, height=2, font="Arial 14",
                           command = lambda : self.quit()).place(x=610, y=425)
        Start = tk.Button(self.root,
                            text = "START", borderwidth=1, relief="solid", bg="#cceae3", width=20, height=3, font="Arial 14",
                            command = lambda : self.Starts()).place(x=320, y=200)
        InstTitle = tk.Label()
        Inst = tk.Label()
        self.root.mainloop()

    def quit(self):
        self.root.destroy()

    def Starts(self):
        self.root.destroy()
        second = Question()

class Question():

    def __init__(self):
        global Correct
        global skips
        global i
        global ButtonPressed
        while i < 11 + skips:
            if len(ButtonsText[i]) == 3:
                ButtonPressed = False
                self.Setup3btn(Questions[i], ButtonsText[i][0], ButtonsText[i][1], ButtonsText[i][2], Answers[i], i)
            elif len(ButtonsText[i]) == 2:
                ButtonPressed = False
                self.Setup2btn(Questions[i], ButtonsText[i][0], ButtonsText[i][1], Answers[i], i)
            else:
                quit()
        third = FinalScreen()

    def Setup3btn(self, Question, ButtonOne, ButtonTwo, ButtonThree, Answer, LCV):
        global skips
        self.root = tk.Tk()
        self.root.geometry('800x500')
        self.root.configure(bg='#cfe2f3')
        self.root.title("Percentages Math Quiz")
        QuestNum = tk.Label(self.root, text="Question " + str(LCV - skips) + "/10", bg="#cceae3", font="Arial 14", width=15,
                            height=2, borderwidth=1, relief="solid").place(x=5, y=5)
        Quest = tk.Label(self.root, text=Question, bg="#cceae3", font="Arial 18", width=28, height=3, borderwidth=1,
                         relief="solid").place(x=210, y=150)

        ButtonA = tk.Button(self.root,
                            text=str(ButtonOne), bg="#cceae3", font="Arial 18", borderwidth=1, relief="solid",
                            width=6, height=1,
                            command=lambda: self.Buttons(Answer, "a")).place(x=260, y=260)

        ButtonB = tk.Button(self.root,
                            text=str(ButtonTwo), bg="#cceae3", font="Arial 18", borderwidth=1, relief="solid",
                            width=6, height=1,
                            command=lambda: self.Buttons(Answer, "b")).place(x=360, y=260)

        ButtonC = tk.Button(self.root,
                            text=str(ButtonThree), bg="#cceae3", font="Arial 18", borderwidth=1, relief="solid",
                            width=6, height=1,
                            command=lambda: self.Buttons(Answer, "c")).place(x=460, y=260)
        SkipButton = tk.Button(self.root,
                               text="Skip Question", bg="#cceae3", font="Arial 16", borderwidth=1, relief="solid",
                               width=12, height=1,
                               command=lambda: self.Skip()).place(x=640, y=450)
        if ButtonPressed == True:
            return
        else:
            self.root.mainloop()
        return

    def Setup2btn(self, Question, Button1, Button2, Answer, LCV):
        global skips
        self.root = tk.Tk()
        self.root.geometry('800x500')
        self.root.configure(bg='#cfe2f3')
        self.root.title("Percentages Math Quiz")
        QuestNum = tk.Label(self.root, text="Question " + str(LCV - skips) + "/10", bg="#cceae3", font="Arial 14", width=15,
                            height=2, borderwidth=1, relief="solid").place(x=5, y=5)
        Quest = tk.Label(self.root, text=Question, bg="#cceae3", font="Arial 18", width=28, height=3, borderwidth=1,
                         relief="solid").place(x=210, y=150)

        ButtonA = tk.Button(self.root,
                            text=str(Button1), bg="#cceae3", font="Arial 18", borderwidth=1, relief="solid",
                            width=6, height=1,
                            command=lambda: self.Buttons(Answer, "true")).place(x=320, y=260)

        ButtonB = tk.Button(self.root,
                            text=str(Button2), bg="#cceae3", font="Arial 18", borderwidth=1, relief="solid",
                            width=6, height=1,
                            command=lambda: self.Buttons(Answer, "false")).place(x=420, y=260)

        SkipButton = tk.Button(self.root,
                               text="Skip Question", bg="#cceae3", font="Arial 16", borderwidth=1, relief="solid",
                               width=12, height=1,
                               command=lambda: self.Skip()).place(x=640, y=450)
        if ButtonPressed == True:
            return
        else:
            self.root.mainloop()
        return

    def Buttons(self, Answer, ButtonTypes):
        global ButtonPressed
        if Answer == ButtonTypes:
            ButtonPressed = True
            global Correct
            global i
            Correct = Correct + 1
            i = i + 1
        elif Answer != ButtonTypes:
            i = i + 1
            ButtonPressed = True
        self.root.destroy()
        second = Question()
        return

    def Skip(self):
        global ButtonPressed
        ButtonPressed = True
        global skips
        global i
        if skips < 3:
            skips = skips + 1
            i = i + 1
            self.root.destroy()
            second = Question()
            return
        else:
            ButtonPressed = False
            return

class FinalScreen():
    def __init__(self):

        global Correct
        percents = Correct/10
        if percents < 0.5:
            Grade = "N/A"
        elif percents > 0.49 and percents < 0.7:
            Grade = "A"
        elif percents > 0.69 and percents < 0.9:
            Grade = "M"
        elif percents > 0.89 and percents < 1.01:
            Grade = "E"
        else:
            quit()
        self.root = tk.Tk()
        self.root.geometry('800x500')
        self.root.configure(bg='#cfe2f3')
        self.root.title("Percentages Math Quiz")

        Title = tk.Label(self.root, text="Results", bg="#cceae3", font="Arial 18", width=15,
                         height=2, borderwidth=1, relief="solid").place(x=330, y=120)
        Result = tk.Label(self.root, text=Grade + "\n" + "You Got: " + str(Correct) + "/10", bg="#cceae3", font="Arial 20",
                          width=20,
                          height=5, borderwidth=1, relief="solid").place(x=270, y=200)

        TryAgn = tk.Button(self.root, text="TRY AGAIN", bg="#cceae3", font="Arial 16", borderwidth=1, relief="solid",
                           width=12, height=1,
                           command=lambda: self.tryAgain()).place(x=640, y=450)

        QuitButton = tk.Button(self.root, text="QUIT", bg="#cceae3", font="Arial 16", borderwidth=1, relief="solid",
                               width=12, height=1, command=lambda: quit()).place(x=10, y=450)

        self.root.mainloop()

    def tryAgain(self):
        self.root.destroy()
        StartScreen()

app = StartScreen()


# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""

#import numpy as np ## not used in this script for now
import pandas as pd
from datetime import datetime
import os
import glob
from ExecQuery import ExecQuery
#import quandl ## later imported in the function
#import zipfile ## later imported in the function

from config import get_data_config

def main():
    GetData(get_data_config)

#########################################
### The Function
#########################################
def GetData(get_data_config):
    # the conf
    data_dir = get_data_config['Quandl_partial_dir']
    api_key = get_data_config['Quandl_api_key']
    col_names = get_data_config['Quandl_col_names']
    db_name = get_data_config['mysql_database']
    table_name = get_data_config['mysql_table_name']

    raw_dir = data_dir + 'raw/'
    proc_dir = data_dir + 'proc/'

    raw_file_list = DumpBulkDataFromQuandl(raw_dir, api_key)
    print(raw_file_list)

    proc_file_dict = ProcessData(proc_dir, raw_file_list, col_names, True)
    print(proc_file_dict)

    LoadData(proc_file_dict, db_name, table_name, True)

    #historical
    #ProcessData('E:/Work/PartTime/DQ_Project/data/QuandlWiki/hist/proc/',['E:/Work/PartTime/DQ_Project/data/QuandlWiki/hist/raw/WIKI_PRICES.csv'], col_names)
    #LoadData(['E:/Work/PartTime/DQ_Project/data/QuandlWiki/hist/proc/proc_WIKI_PRICES.csv'], db_name, table_name)

#########################################
### Helper Functions
#########################################

### 1. DumpBulkDataFromQuandl ###########
def DumpBulkDataFromQuandl(raw_dir, api_key):
    # defininations
    zip_file_name = raw_dir + datetime.strftime(datetime.now(),'%Y%m%d.%H%M%S') + '.zip'

    # bulkdownload
    import quandl
    quandl.ApiConfig.api_key = api_key
    quandl.bulkdownload("WIKI", download_type="partial", filename=zip_file_name)

    # remove all the existing csv files
    to_remove_list = glob.glob(raw_dir + '*.csv')
    for f in to_remove_list:
        os.remove(f)

    # upzip and rename
    import zipfile
    zip_ref = zipfile.ZipFile(zip_file_name, 'r')
    zip_ref.extractall(raw_dir)
    zip_ref.close()

    raw_file_list = glob.glob(raw_dir + '*.csv')
    raw_file_list = [f.replace('\\','/') for f in raw_file_list]
    return raw_file_list

### 2. ProcessData ######################
def ProcessData(proc_dir, raw_file_list, col_names, keep_lastest=False):
    proc_file_dict = {}

    for raw_file_name in raw_file_list:
        #raw_file_name = raw_file_list[0] ###DEBUG
        #keep_lastest=True ###DEBUG

        # read the data
        data = pd.read_csv(raw_file_name, header=None, names = col_names)
        #data = pd.read_csv(raw_file_name) ###DEBUG, hist

        if keep_lastest:
            # only keep the data when date = date in the file name
            datex = ((raw_file_name.split('/')[-1]).split('.')[0]).split('_')[1]
            datex = datetime.strftime(datetime.strptime(datex,'%Y%m%d'), '%Y-%m-%d')
            data = data[data['date']==datex]

        # transform the format of the data
        data['date'] = pd.to_datetime(data['date']).dt.strftime('%Y/%m/%d')

        # generate the file name based on the raw file name
        proc_file_name = proc_dir + 'proc_' + raw_file_name.split('/')[-1]

        # output with nan being 'NULL'
        data.to_csv(proc_file_name, na_rep='NULL', index=False, header=None)

        proc_file_dict.update({proc_file_name:datex})

    return proc_file_dict

### 3. LoadData #########################
def LoadData(proc_file_dict, db_name, table_name, delete=False):
    for f in proc_file_dict.keys():
        if delete:
            del_query = "delete from " + table_name + " where date = '" + proc_file_dict[f] + "'; commit;"
            print(del_query)
            ExecQuery(db_name, del_query)

        load_query = """
                     LOAD DATA INFILE '""" + f.replace('/','\\\\') + """'
                     INTO TABLE """ + table_name + """
                     COLUMNS TERMINATED BY ','
                     OPTIONALLY ENCLOSED BY '"'
                     ESCAPED BY '"'
                     LINES TERMINATED BY '\\r\\n'
                     #IGNORE 1 ROWS
                     ;commit;
                     """
        print(load_query)
        ExecQuery(db_name, load_query)

#########################################
### Execute if main
#########################################
if __name__ == '__main__':
    main()

#########################################
### Examples/Explanations
#########################################

def DumpDfDataFromQuandl(sd, ed, ticker):
    # bulkdownload
    import quandl
    quandl.ApiConfig.api_key = get_data_config['Quandl_api_key']
    df = quandl.get_table('WIKI/PRICES'
                          , qopts = {'columns': ['ticker', 'date', 'adj_close']}
                          , date={'gte': sd, 'lte': ed}
                          , ticker=ticker
                          , paginate=True)
    return df

def DumpDataFromQuandlExamples():
    import quandl

    ### Here are a few ways to dump the data from Quandl, to data frame
    # 0. load the api_key
    quandl.ApiConfig.api_key = get_data_config['Quandl_api_key']

    # 1. get_table method: can get one day or multiple days of data
    print(quandl.get_table('WIKI/PRICES', date='2018-03-28', ticker='AAPL'))

    print(quandl.get_table('WIKI/PRICES', ticker = ['AAPL', 'MSFT', 'WMT'],
                        #qopts = { 'columns': ['ticker', 'date', 'adj_close'] },
                        date = { 'gte': '2016-12-15', 'lte': '2016-12-31' },
                        paginate=True))

    # 2. get method: mostly for a time series
    print(quandl.get("WIKI/AAPL", trim_start = "2012-12-21", trim_end = "2013-01-01"))

    # 3. bulkdownload method: download all the sybmbols as of the last available date
    print(quandl.bulkdownload("WIKI", download_type="partial", filename="E:/Work/PartTime/DQ_Project/temp/WIKI.zip"))


# -*- coding: utf-8 -*-
"""
@author: Wei, Shuowen

https://leetcode.com/problems/expression-add-operators/

solution: https://leetcode.com/problems/expression-add-operators/submissions/

LC241, LC282, LC43, LC224, LC227, LC772, LC150
"""
# https://leetcode.com/problems/expression-add-operators/discuss/572099/C%2B%2BJavaPython-Backtracking-and-Evaluate-on-the-fly-Clean-and-Concise
class Solution(object):
    def addOperators(self, num, target):
        """
        :type num: str
        :type target: int
        :rtype: List[str]
        """
        res = []
        def backtracking(path, start, val, pre_val):
            if start == len(num):
                if val == target:
                    res.append(path)
                return
            for i in range(start, len(num)):
                if num[start] == '0' and i > start: # must have i > start: "105", 5
                    break # Skip leading zero number
                cur_num = int(num[start: i + 1])
                if start == 0:
                    backtracking(path + str(cur_num), i+1, val + cur_num, cur_num)
                else:
                    backtracking(path+'+'+str(cur_num), i+1, val + cur_num, cur_num)
                    backtracking(path+'-'+str(cur_num), i+1, val - cur_num, -cur_num)
                    # e.g: 1+2*3*4
                    backtracking(path+'*'+str(cur_num), i+1, val - pre_val + pre_val*cur_num, pre_val*cur_num)

        backtracking('', 0, 0, 0)
        return res

        # solution 2: Time Limit Exceeded
        def helper(s): # s is string
            num = 0
            sign = '+'
            stack = []
            for i, char in enumerate(s):
                char = s[i]
                if char.isdigit():
                    num = 10*num + int(char)
                if (not char.isdigit() and char != ' ') or i == len(s) - 1:
                    if sign == '+':
                        stack.append(num)
                    elif sign == '-':
                        stack.append(-num)
                    elif sign == '*':
                        stack[-1] = stack[-1] * num
                    sign = char
                    num = 0
            return sum(stack)

        res = []
        def backtrack(num, start, path):
            if len(num) == start:
                if helper(path) == target:
                    res.append(path)
                return
            for i in range(start, len(num)):
                if i > start and num[start] == '0':
                    break
                cur_num = int(num[start: i + 1])
                if start == 0:
                    # First num, pick it without adding any operator, e.g. '105', 5
                    backtrack(num, i+1, path+str(cur_num))
                else:
                    backtrack(num, i+1, path+'+'+str(cur_num))
                    backtrack(num, i+1, path+'-'+str(cur_num))
                    backtrack(num, i+1, path+'*'+str(cur_num))
        backtrack(num, 0, '')
        return res