#! /usr/bin/env python
# -*- coding: utf-8 -*-

import os
import numpy as np
import scipy.optimize
import Manyo.SAS as ms
import Manyo as mm
import math
import utsusemi.SAS.ana.Reduction.HistBaseCommands as HBC
import utsusemi.SAS.ana.Reduction.CorrBaseCommands as CBC
"""
Base Functions for SAS
Trans.py
- TransmittanceCorrectionExp
- TransmittanceCorrectionPoly3
- FitExponentialWoBase
- FitExponential
- FitPolynomial3
- GetTransNormByMonCounts
- GetTransNormByMonCountsTS
- GetTransNormByMon
- GetTransNormByMonTS
- GetCalcTransmittance
- CreateCoefficientSet
- SetCoeffToTransmittance
"""
#########################################
def TransmittanceCorrectionExp(dat, params, calcError=False, useAngDep=False):
    """
    Transmittance correction by an exponential function, which is p0*exp(-p1*lambda)+p2.
    @param dat      (ElementContainerMatrix) or list of ElementContainerMatrix
    @param params   (PyObject) Function parameters and their errors
    @param calcError    (bool) True: Calculate an error w/ errors of parameters.
                            False: Calculate it w/o errors of parameters.
    @param useAngDep (bool) True: use angle dependence of calculation for transmittance correction
    """
    if type(dat)==list:
        dat_list=dat
    else:
        dat_list=[dat]
        params_list = [params]
    for a_dat,one_params in zip(dat_list,params_list):
        trans = ms.TransmittanceCorrection(a_dat)
        p = mm.ListToDoubleVector(one_params[0])
        e = mm.ListToDoubleVector(one_params[1])
        trans.SetErrorPropagation(calcError)
        trans.SetParameters(p, e, trans.TRANSMIT_TYPE_EXP, useAngDep)
        trans.Execute()
        hh = a_dat.PutHeaderPointer()
        hh.AddHeader("TransmittanceParameter", p)
        hh.AddHeader("TransmittanceParameterError", e)
        hh.AddHeader("TransmittanceFitFunc", "Exponential")
        del trans

#########################################
def TransmittanceCorrectionPoly3(dat, params, calcError=False, useAngDep=False):
    """
    Transmittance correction by a degree 3 polynomial function, which is p0+p1*lamb+p2*lamb^2+p3*lamb^3.
    @param dat      (ElementContainerMatrix)
    @param params   (PyObject) Function parameters and their errors
    @param calcError    (bool) True: Calculate an error w/ errors of parameters.
                            False: Calculate it w/o errors of parameters.
    @param useAngDep (bool) True: use angle dependence of calculation for transmittance correction
    """
    if type(dat)==list:
        dat_list = dat
        params_list = params
    else:
        dat_list=[dat]
        params_list = [params]
    for a_dat,one_params in zip(dat_list,params_list):
        print("one_params=",one_params)
        trans = ms.TransmittanceCorrection(a_dat)
        p = mm.ListToDoubleVector(one_params[0])
        e = mm.ListToDoubleVector(one_params[1])
        trans.SetErrorPropagation(calcError)
        trans.SetParameters(p, e, trans.TRANSMIT_TYPE_POLY3, useAngDep)
        trans.Execute()
        hh = a_dat.PutHeaderPointer()
        hh.AddHeader("TransmittanceParameter", p)
        hh.AddHeader("TransmittanceParameterError", e)
        hh.AddHeader("TransmittanceFitFunc", "Polynomial3")
        del trans

#########################################
def TransmittanceCorrPolyApproximation(dat, params, calcError=False):
    """
    Transmittance correction by a degree 3 polynomial function, which is exp(-S*thick) where S = p0+p1*lamb+p2*lamb^2+p3*lamb^3.
    @param dat      (ElementContainerMatrix)
    @param params   (PyObject) Function parameters and their errors
    @param calcError    (bool) True: Calculate an error w/ errors of parameters.
                            False: Calculate it w/o errors of parameters.
    """
    print("params = ", params)
    if type(dat)==list:
        dat_list = dat
        params_list = params
    else:
        dat_list=[dat]
        params_list = [params]
    for a_dat,one_params in zip(dat_list,params_list):
        print("one_params=",one_params)
        a_dat.PutHeaderPointer().Add("TransmittancePolyApproxThick", params[2])
        trans = ms.TransmittanceCorrection(a_dat)
        p = mm.ListToDoubleVector(one_params[0])
        e = mm.ListToDoubleVector(one_params[1])
        trans.SetErrorPropagation(calcError)
        trans.SetParameters(p, e, trans.TRANSMIT_TYPE_POLYAPPROX, False)
        trans.Execute()
        hh = a_dat.PutHeaderPointer()
        hh.AddHeader("TransmittanceParameter", p)
        hh.AddHeader("TransmittanceParameterError", e)
        hh.AddHeader("TransmittanceFitFunc", trans.TRANSMIT_TYPE_POLYAPPROX)
        del trans

#########################################
def PreprocessForFit(dat, iniVals):
    """
    Preprocessing for fitting.
    @params dat         (ElementContainer)
    @params iniVals     (str) initial parameters separated with space
    @retval (list, list, list, list) lists of lambda, intensity, error and initial parameters
    """
    x = dat.PutList("Lamb")
    y = dat.PutList("Intensity")
    e = dat.PutList("Error")
    ini_str_list = iniVals.split(" ")
    ini_list = []
    for s in ini_str_list:
        if s != "":
            ini_list.append( float(s) )
    return x, y, e, ini_list

#########################################
def PostprocessForFit(dat, p1, err, func_name):
    """
    Postprocessing for fitting.
    @params dat         (ElementContainer)
    @params p1          (list) - list of fitting result
    @params err         (list) - list of fitting error
    @params func_name   (str) - fitting function name
    """
    dat.AddToHeader("TransmittanceParameter", mm.ListToDoubleVector(p1))
    dat.AddToHeader("TransmittanceParameterError", mm.ListToDoubleVector(err))
    dat.AddToHeader("TransmittanceFitFunc", func_name)

#########################################
def FitExponentialWoBase(dat,iniVals="1.0 0.01",weighted=True):
    """
    An exponential fitting. The fitting function is p0*exp(-p1*lambda).
    @params dat         (ElementContainer) or list of ElementContainer
    @params iniVals     (str) initial parameters separated with space
    @params weighted    (bool) using weighted least squares
    @retval PyObj
    """
    if type(dat)==list:
        dat_list = dat
    else:
        dat_list = [dat]
    p1_list=[]
    err_list=[]
    for a_dat in dat_list:
        f = lambda p, x: p[0]*np.exp(-p[1]*x)
        x, y, e, ini_list = PreprocessForFit(a_dat, iniVals)
        p1, err = Fit(f, ini_list, x, y, e, weighted, omitZero=False)
        p1.append(0.0)
        err.append(0.0)
        PostprocessForFit(a_dat, p1, err, "Exponential")
        p1_list.append(p1)
        err_list.append(err)
    if len(p1_list)==1:
        return p1_list[0], err_list[0], "exp"
    else:
        return p1_list, err_list, "exp"

#########################################
def FitExponential(dat,iniVals="1.0 0.01 0.0001",weighted=True):
    """
    An exponential fitting. The fitting function is p0*exp(-p1*lambda)+p2.
    @params dat         (ElementContainer) or list of ElementContainer
    @params iniVals     (str) initial parameters separated with space
    @params weighted    (bool) using weighted least squares
    @retval PyObj
    """
    if type(dat)==list:
        dat_list=dat
    else:
        dat_list=[dat]
    p1_list=[]
    err_list=[]
    for a_dat in dat_list:
        f = lambda p, x: p[0]*np.exp(-p[1]*x) + p[2]
        x, y, e, ini_list = PreprocessForFit(a_dat, iniVals)
        p1, err = Fit(f, ini_list, x, y, e, weighted, omitZero=False)
        PostprocessForFit(a_dat, p1, err, "Exponential")
        p1_list.append(p1)
        err_list.append(err)
    if len(p1_list)==1:
        return p1_list[0], err_list[0], "exp"
    else:
        return p1_list, err_list, "exp"

#########################################
def FitPolyApproximation(dat, thick=1.0, iniVals="0.0001 0.0001 0.0001 0.0001", weighted=True):
    """
    An exponential fitting. The fitting function is
      exp(-(P1 + P2 * Lambda + P3 * Lambda^2 + P4 * Lambda^3) * T)
    @params dat         (ElementContainer) or list of ElementContainer
    @params thick       (float) thickness of sample [cm]
    @params iniVals     (str) initial parameters separated with space
    @params weighted    (bool) using weighted least squares
    @retval PyObj
    """
    if type(dat) == list:
        dat_list = dat
    else:
        dat_list = [dat]
    p1_list = []
    err_list = []
    for a_dat in dat_list:
        f = lambda p, x, t: np.exp(-(p[0] + (p[1] * x) + (p[2] * x * x) + (p[3] * x * x * x)) * t)
        x, y, e, ini_list = PreprocessForFit(a_dat, iniVals)
        #p1, err = Fit(f, ini_list, x, y, e, weighted, omitZero=False)
        p1, err = FitPolyApprox(f, ini_list, x, y, e, thick, weighted, omitZero=False)
        trans = ms.TransmittanceCorrection()
        a_dat.AddToHeader("TransmittancePolyApproxThick", thick)
        PostprocessForFit(a_dat, p1, err, trans.TRANSMIT_TYPE_POLYAPPROX)
        print("Fitting : exp(-(p0 + (p1 * lam) + (p2 * lam^2) + (p3 * lam^3)) * thick)")
        for i in range(4):
            print("p{} = {} +- {}".format(i, p1[i], err[i]))
        p1_list.append(p1)
        err_list.append(err)
    if len(p1_list) == 1:
        return p1_list[0], err_list[0], thick, trans.TRANSMIT_TYPE_POLYAPPROX
    else:
        return p1_list, err_list, thick, trans.TRANSMIT_TYPE_POLYAPPROX

#########################################
def FitPolynomial3(dat,iniVals="0.3 -0.05 0.004 -0.0001",weighted=True):
    """
    A degree 3 polynomial fitting. The fitting function is p0+p1*lamb+p2*lamb^2+p3*lamb^3.
    @params dat         (ElementContainer) or list of ElementContainer
    @params iniVals     (str) initial parameters separated with space
    @params weighted    (bool) using weighted least squares
    @retval PyObj
    """
    if type(dat)==list:
        dat_list=dat
    else:
        dat_list=[dat]
    ret_list = []
    for a_dat in dat_list:
        f = lambda p, x: p[0] + p[1]*x + p[2]*x**2 + p[3]*x**3
        x, y, e, ini_list = PreprocessForFit(a_dat, iniVals)
        p1, err = Fit(f, ini_list, x, y, e, weighted, omitZero=False)
        PostprocessForFit(a_dat, p1, err, "Polynomial3")
        ret_list.append( (p1,err,"poly3" ) )
    if len(ret_list)==1:
        return ret_list[0]
    return ret_list

#########################################
def GetTransNormByMonCounts(SmplRunNo=104000, DrctRunNo=104000, AxType="lambda", XRange="0.7 7.6 0.1", frameBoundary=0.0, CalibEffi=True, paramFiles="- -" ):
    """
    Monitor histogram of sample and direct followed by a transmittance calculation.
    @param SmplRunNo     (int)    Sample measurement run number
    @param DrctRunNo     (int)    Direct beam measurement run number
    @param AxType        (string)  Type must be "tof', "lambda" or "lamda2"
    @param XRange        (string) "startX endX widthX" separated with space (not comma)
    @param frameBoundary (float)
    @param CalibEffi     (bool)    Monitor-detector efficiency calibration
    @param paramFiles    (string) "<wiringInfo> <detectorInfo>[ <caseInfo>]"
    @retval list of Tr
    """
    import Com
    s = XRange.split()
    norm_range = "%s %s"%(s[0], s[1])

    smpl1s = HBC.GetMonHistSAS(SmplRunNo, 1, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi, paramFiles=paramFiles)
    smpl2s = HBC.GetMonHistSAS(SmplRunNo, 2, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi, paramFiles=paramFiles)
    if type(smpl1s)!=list:
        smpl1s = [smpl1s]
        smpl2s = [smpl2s]
    for smpl1, smpl2 in zip(smpl1s,smpl2s):
        CBC.NormByMonCounts(smpl2, smpl1, "Lamb", norm_range, 1.0)

    drct1 = HBC.GetMonHistSAS(DrctRunNo, 1, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    drct2 = HBC.GetMonHistSAS(DrctRunNo, 2, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    CBC.NormByMonCounts(drct2, drct1, "Lamb", norm_range, 1.0)

    ret = []
    for smpl2 in smpl2s:
        ret.append( Com.ExecFunctionCode("A/B", smpl2, drct2) )
    if len(ret)==1:
        return ret[0]
    return ret

#########################################
def GetTransNormByMonCountsTS(SmplRunNo=104000, DrctRunNo=104000, AxType="lambda", XRange="0.7 7.6 0.1", frameBoundary=0.0, CalibEffi=True, SmplTSlice="-1 -1", DrctTSlice="-1 -1", paramFiles="- -"):
    """
    Monitor histogram of sample and direct followed by a transmittance calculation.
    @param SmplRunNo     (int)    Sample measurement run number
    @param DrctRunNo     (int)    Direct beam measurement run number
    @param AxType        (string)  Type must be "tof', "lambda" or "lamda2"
    @param XRange        (string) "startX endX widthX" separated with space (not comma)
    @param frameBoundary (float)
    @param CalibEffi     (bool)    Monitor-detector efficiency calibration
    @param SmplTSlice    (string) Time Slice parameters for Sample run
    @param DrctTSlice    (string) Time Slice parameters for Directrun
    @param paramFiles    (string) "<wiringInfo> <detectorInfo>[ <caseInfo>]
    @retval Tr
    """
    import Com
    s = XRange.split()
    norm_range = "%s %s"%(s[0], s[1])

    smpl1s = HBC.GetMonHistSAS(SmplRunNo, 1, AxType, XRange, 0, SmplTSlice, str(frameBoundary), CalibEffi, paramFiles=paramFiles)
    smpl2s = HBC.GetMonHistSAS(SmplRunNo, 2, AxType, XRange, 0, SmplTSlice, str(frameBoundary), CalibEffi, paramFiles=paramFiles)
    if type(smpl1s)!=list:
        smpl1s = [smpl1s]
        smpl2s = [smpl2s]
    for smpl1,smpl2 in zip(smpl1s,smpl2s):
        CBC.NormByMonCounts(smpl2, smpl1, "Lamb", norm_range, 1.0)

    drct1 = HBC.GetMonHistSAS(DrctRunNo, 1, AxType, XRange, 0, DrctTSlice, str(frameBoundary), CalibEffi)
    drct2 = HBC.GetMonHistSAS(DrctRunNo, 2, AxType, XRange, 0, DrctTSlice, str(frameBoundary), CalibEffi)
    CBC.NormByMonCounts(drct2, drct1, "Lamb", norm_range, 1.0)

    ret = []
    for smpl2 in smpl2s:
        ret.append(Com.ExecFunctionCode("A/B", smpl2, drct2))
    if len(ret)==1:
        return ret[0]
    return ret

#########################################
def GetTransNormByMon(SmplRunNo=104000, DrctRunNo=104000, Xrange="0.7 7.6 0.1 dL", frameBoundary="0.0", CalibEffi=True, useMon="MON1", paramFiles="- -" ):
    """
    Monitor histogram of sample and direct followed by a transmittance calculation.
    @param SmplRunNo     (int)    Sample measurement run number
    @param DrctRunNo     (int)    Direct beam measurement run number
    @param Xrange        (string)  "startX endX widthX type" separated with space (not comma) : type=[dL, dL/L, tof]
    @param frameBoundary (float)
    @param CalibEffi     (bool)    Monitor-detector efficiency calibration
    @param useMon        (strng)  Upstream Monitor (MON1 or MON3)
    @param paramFiles    (string) "<wiringInfo> <detectorInfo>[ <caseInfo>]
    @retval Tr
    """
    import Com
    s = Xrange.split()
    norm_range = "%s %s"%(s[0], s[1])

    x_range = Xrange.split( " " )
    AxType=""
    if len(x_range)!=4:
        raise UserWarning("Xrange is invalid.")
    if x_range[3] == "dL":
        AxType = "lambda"
    elif x_range[3] == "dL/L":
        AxType = "lambda2"
    elif x_range[3] == "tof":
        AxType = "tof"
    else:
        raise UserWarning("Xrange is invalid.")
    XRange = "%s %s %s"%(x_range[0],x_range[1],x_range[2])

    monUpStream = 1
    if useMon=="MON1":
        monUpStream = 1
    elif useMon=="MON3":
        monUpStream = 3
    else:
        raise UserWarning("useMon is invalid.")

    smpl1s = HBC.GetMonHistSAS(SmplRunNo, monUpStream, AxType, XRange, 0, "-1 -1", frameBoundary, CalibEffi, paramFiles=paramFiles)
    smpl2s = HBC.GetMonHistSAS(SmplRunNo, 2, AxType, XRange, 0, "-1 -1", frameBoundary, CalibEffi, paramFiles=paramFiles)
    if type(smpl1s)!=list:
        smpl1s = [smpl1s]
        smpl2s = [smpl2s]
    for smpl1,smpl2 in zip(smpl1s,smpl2s):
        CBC.NormByMonCounts(smpl2, smpl1, "Lamb", norm_range, 1.0)

    drct1 = HBC.GetMonHistSAS(DrctRunNo, monUpStream, AxType, XRange, 0, "-1 -1", frameBoundary, CalibEffi)
    drct2 = HBC.GetMonHistSAS(DrctRunNo, 2, AxType, XRange, 0, "-1 -1", frameBoundary, CalibEffi)
    CBC.NormByMonCounts(drct2, drct1, "Lamb", norm_range, 1.0)

    ret = []
    for smpl2 in smpl2s:
        ret.append(Com.ExecFunctionCode("A/B", smpl2, drct2))
    if len(ret)==1:
        return ret[0]
    return ret

#########################################
def GetTransNormByMonTS(SmplRunNo=104000, DrctRunNo=104000, Xrange="0.7 7.6 0.1 dL", frameBoundary="0.0", CalibEffi=True, SmplTSlice="-1 -1", DrctTSlice="-1 -1", useMon="MON1", paramFiles="- -"):
    """
    Monitor histogram of sample and direct followed by a transmittance calculation.
    @param SmplRunNo     (int)    Sample measurement run number
    @param DrctRunNo     (int)    Direct beam measurement run number
    @param Xrange        (string)  "startX endX widthX type" separated with space (not comma) : type=[dL, dL/L, tof]
    @param frameBoundary (float)
    @param CalibEffi     (bool)    Monitor-detector efficiency calibration
    @param SmplTSlice    (string) Time Slice parameters for Sample run
    @param DrctTSlice    (string) Time Slice parameters for Directrun
    @param useMon        (strng)  Upstream Monitor (MON1 or MON3)
    @param paramFiles    (string) "<wiringInfo> <detectorInfo>[ <caseInfo>]
    @retval Tr
    """
    import Com
    s = Xrange.split()
    norm_range = "%s %s"%(s[0], s[1])

    x_range = Xrange.split( " " )
    AxType=""
    if len(x_range)!=4:
        raise UserWarning("Xrange is invalid.")
    if x_range[3] == "dL":
        AxType = "lambda"
    elif x_range[3] == "dL/L":
        AxType = "lambda2"
    elif x_range[3] == "tof":
        AxType = "tof"
    else:
        raise UserWarning("Xrange is invalid.")
    XRange = "%s %s %s"%(x_range[0],x_range[1],x_range[2])

    monUpStream = 1
    if useMon=="MON1":
        monUpStream = 1
    elif useMon=="MON3":
        monUpStream = 3
    else:
        raise UserWarning("useMon is invalid.")

    smpl1s = HBC.GetMonHistSAS(SmplRunNo, monUpStream, AxType, XRange, 0, SmplTSlice, frameBoundary, CalibEffi, paramFiles=paramFiles)
    smpl2s = HBC.GetMonHistSAS(SmplRunNo, 2, AxType, XRange, 0, SmplTSlice, frameBoundary, CalibEffi, paramFiles=paramFiles)
    if type(smpl1s)!=list:
        smpl1s = [smpl1s]
        smpl2s = [smpl2s]
    for smpl1,smpl2 in zip(smpl1s,smpl2s):
        CBC.NormByMonCounts(smpl2, smpl1, "Lamb", norm_range, 1.0)

    drct1 = HBC.GetMonHistSAS(DrctRunNo, monUpStream, AxType, XRange, 0, DrctTSlice, frameBoundary, CalibEffi)
    drct2 = HBC.GetMonHistSAS(DrctRunNo, 2, AxType, XRange, 0, DrctTSlice, frameBoundary, CalibEffi)
    CBC.NormByMonCounts(drct2, drct1, "Lamb", norm_range, 1.0)

    ret = []
    for smpl2 in smpl2s:
        ret.append(Com.ExecFunctionCode("A/B", smpl2, drct2))
    if len(ret)==1:
        return ret[0]
    return ret


#########################################
def GetCalcTransmittance(dat):
    """
    Get trnasmittance data using a fitting result
    @param dat      (ElementContainer) Observed transmittance which was used by fitting facades
    @retval Tr_calc
    """
    lambs = dat.PutList("Lamb")
    lambdas = dat.PutList("Lambda")
    hh = dat.PutHeader()
    p = list(hh.PutDoubleVector("TransmittanceParameter"))
    e = list(hh.PutDoubleVector("TransmittanceParameterError"))
    f = hh.PutString("TransmittanceFitFunc")
    trans = ms.TransmittanceCorrection()
    if f == "Exponential":
        ys = [p[0]*math.exp(-p[1]*x)+p[2] for x in lambs]
        es = [math.sqrt( (e[0]**2+(x**2)*(p[0]**2)*(e[1]**2)) * math.exp(-2*p[1]*x) + e[2]**2) for x in lambs]
    elif f == "Polynomial3":
        ys = [ p[0]+p[1]*x+p[2]*x**2+p[3]*x**3 for x in lambs]
        es = [math.sqrt( e[0]**2 + (x*e[1])**2 + (e[2]*x*x)**2 + (e[3]*x*x*x)**2 ) for x in lambs]
    elif f == trans.TRANSMIT_TYPE_POLYAPPROX:
        t = hh.PutDouble("TransmittancePolyApproxThick")
        ys = [ math.exp(-(p[0] + p[1] * x + p[2] * x * x + p[3] * x * x * x) * t) for x in lambs]
        es = []
        for x in lambs:
            st = (p[0] + p[1] * x + p[2] * x * x + p[3] * x * x * x) * t
            ex = -t * math.exp(-st)
            ee = (ex * ex) * ((e[0] * e[0]) + (e[1] * e[1] * x * x) + (e[2] * e[2] * x * x * x * x) + (e[3] * e[3] * x * x * x * x * x * x))
            es.append(math.sqrt(ee))
    else:
        raise UserWarning("Unknown fitting function name.")
    ec = mm.ElementContainer(hh)
    ec.Add("Lamb", lambs, "Ang.")
    ec.Add("Lambda", lambdas, "Ang.")
    ec.Add("Intensity", ys)
    ec.Add("Error", es)
    ec.SetKeys("Lambda", "Intensity", "Error")
    return ec

#########################################
def CreateCoefficientSet(coeff="1.0 0.01 0.0001", err="0.1 0.001 0.00001", funcType="exp"):
    """
    Create a coefficient set
    @param coeff    (str) coefficient separated with space
    @param err      (str) error separated with space
    @param funcType (str) function type. "exp" or "poly3"
    @retval PyObj
    """
    c = coeff.split(" ")
    e = err.split(" ")
    if len(c) != len(e):
        raise UserWarning("Num. of parameters dose not match.")
    if funcType not in ["exp", "poly3"]:
        raise UserWarning("Undefined function type.")
    try:
        c_num = list(map(float, c))
    except ValueError:
        raise UserWarning("param: %s"%sys.exc_info()[1])
    try:
        e_num = list(map(float, e))
    except ValueError:
        raise UserWarning("err: %s"%sys.exc_info()[1])
    return c_num, e_num, funcType

########################################
def SetCoeffToTransmittance(dat, params):
    """
    Set a coefficient set to transmittance data container.
    @param dat      (ElementContainer) Observed transmittance
    @param params   (PyObj) Coefficient set
    @retval None
    """
    trans = ms.TransmittanceCorrection()
    if params[2] == "exp":
        if (len(params[0]) != 3) or (len(params[1]) != 3):
            raise UserWarning("Number of parameters dose not match.")
        dat.AddToHeader("TransmittanceFitFunc", "Exponential")
    elif params[2] == "poly3":
        if (len(params[0]) != 4) or (len(params[1]) != 4):
            raise UserWarning("Number of parameters dose not match.")
        dat.AddToHeader("TransmittanceFitFunc", "Polynomial3")
    elif params[2] == trans.TRANSMIT_TYPE_POLYAPPROX:
        dat.AddToHeader("TransmittanceFitFunc", trans.TRANSMIT_TYPE_POLYAPPROX)
    else:
        raise UserWarning("Undefined function type.")
    p = mm.ListToDoubleVector(params[0])
    e = mm.ListToDoubleVector(params[1])
    dat.AddToHeader("TransmittanceParameter", p)
    dat.AddToHeader("TransmittanceParameterError", e)

########################################
def Fit(fitfunc, initialValues, lambValues, intValues, errValues, weighted=True, omitZero=False):
    """
    Fit transmmitance data by a fitting function.

    @param fitfunc (lambda function) a fitting function
    @param initialValues (list) - list of initial values
    @param lambValues (list) - list of lambda
    @param intValues (list) - list of intensity
    @param errValues (list) - list of error
    @params weighted    (bool) using weighted least squares
    @params omitZero (bool) omitting zero data points
    @retval (list, list) - lists of fitting result and error of each parameters
    """
    if weighted:
        errfunc = lambda p, x, y, sigma: (fitfunc(p, x) - y)/sigma
    else:
        errfunc = lambda p, x, y, sigma: fitfunc(p, x) - y

    np_lamb = np.array(lambValues)
    np_int = np.array(intValues)
    np_err = np.array(errValues)
    indicies = np.flatnonzero(np_int)
    if omitZero:
        result = scipy.optimize.leastsq(errfunc, initialValues[:], args=(np_lamb[indicies], np_int[indicies], np_err[indicies]), full_output=True)
    else:
        result = scipy.optimize.leastsq(errfunc, initialValues[:], args=(np_lamb, np_int, np_err), full_output=True)
    if result[-1] not in [1, 2, 3, 4]:
        raise UserWarning("The solution was not found.%s%s"%(os.linesep, result[-2]))
    p1=result[0]                    # solution
    errMat=result[1]                # variance-covariance matrix
    err=np.sqrt(np.diag(errMat))
    fvec=result[2]['fvec']          # residuals
    C = len(initialValues)
    N=len(lambValues) # N: number of data
    D=N-C                           # D: degree of freedom
    chisq=np.sum(fvec**2)/D            # reduced chi-square
    return p1.tolist(), err.tolist()

########################################
def FitPolyApprox(fitfunc, initialValues, lambValues, intValues, errValues, thick=1.0, weighted=True, omitZero=False):
    """
    Fit transmmitance data by a fitting function.

    @param fitfunc (lambda function) a fitting function
    @param initialValues (list) - list of initial values
    @param lambValues (list) - list of lambda
    @param intValues (list) - list of intensity
    @param errValues (list) - list of error
    @param thick (float) thickness of
    @params weighted (bool) using weighted least squares
    @params omitZero (bool) omitting zero data points
    @retval (list, list) - lists of fitting result and error of each parameters
    """
    if weighted:
        errfunc = lambda p, x, y, sigma, t: (fitfunc(p, x, t) - y)/sigma
    else:
        errfunc = lambda p, x, y, sigma, t: fitfunc(p, x, t) - y

    np_lamb = np.array(lambValues)
    np_int = np.array(intValues)
    np_err = np.array(errValues)
    indicies = np.flatnonzero(np_int)
    if omitZero:
        result = scipy.optimize.leastsq(errfunc, initialValues[:], args=(np_lamb[indicies], np_int[indicies], np_err[indicies], thick), full_output=True)
    else:
        result = scipy.optimize.leastsq(errfunc, initialValues[:], args=(np_lamb, np_int, np_err, thick), full_output=True)
    if result[-1] not in [1, 2, 3, 4]:
        raise UserWarning("The solution was not found.%s%s"%(os.linesep, result[-2]))
    p1 = result[0]                    # solution
    errMat = result[1]                # variance-covariance matrix
    err = np.sqrt(np.diag(errMat))
    fvec = result[2]['fvec']          # residuals
    C = len(initialValues)
    N = len(lambValues) # N: number of data
    D = N - C                         # D: degree of freedom
    chisq = np.sum(fvec**2) / D       # reduced chi-square
    return p1.tolist(), err.tolist()

#########################################
def GetTransRPMTNormByMon(SmplRunNo=104000, DrctRunNo=104000, PixelRange="150 150 152 152", AxType="lambda", XRange="0.7 7.6 0.1", frameBoundary=0.0, CalibEffi=True ):
    """
    Monitor histogram of sample and direct followed by a transmittance calculation.
    @param SmplRunNo     (int)    Sample measurement run number
    @param DrctRunNo     (int)    Direct beam measurement run number
    @param PixelRange    (string) "x0 y0 x1 y1"
    @param AxType        (string)  Type must be "tof', "lambda" or "lamda2"
    @param XRange        (string) "startX endX widthX" separated with space (not comma)
    @param frameBoundary (float)
    @param CalibEffi     (bool)    Monitor-detector efficiency calibration
    @retval Tr
    """
    import Com
    s = XRange.split()
    norm_range = "%s %s"%(s[0], s[1])

    smpl1 = HBC.GetMonHistSAS(SmplRunNo, 1, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    #smpl2 = HBC.GetMonHistSAS(SmplRunNo, 2, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    smpl2 =GetSumRPMTPixelArea(SmplRunNo, PixelRange, AxType, XRange, str(frameBoundary) )
    CBC.NormByMonCounts(smpl2, smpl1, "Lamb", norm_range, 1.0)

    drct1 = HBC.GetMonHistSAS(DrctRunNo, 1, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    #drct2 = HBC.GetMonHistSAS(DrctRunNo, 2, AxType, XRange, 0, "-1 -1", str(frameBoundary), CalibEffi)
    drct2 =GetSumRPMTPixelArea(DrctRunNo, PixelRange, AxType, XRange, str(frameBoundary) )
    CBC.NormByMonCounts(drct2, drct1, "Lamb", norm_range, 1.0)

    return Com.ExecFunctionCode("A/B", smpl2, drct2)


#########################################
def GetSumRPMTPixelArea( runNo, PixelRange, AxType, XRange, frameBoundary ):
    if AxType=="lambda":    #"0.7 7.6 0.1 dL"
        Xrange = XRange+" dL"
    elif AxType=="tof":
        Xrange = XRange+" tof"
    elif AxType=="lambda2":
        Xrange = XRange+" dL/L"
    elif AxType=="d":
        Xrange = XRange+" d"
    else:
        raise UserWarning( "Invalid AxType")

    rpmt2 = HBC.GetHistogramSAS(runNo, Xrange, "RPMT", TimeSlice="-1 -1", frameBoundary=frameBoundary, isTimeFoc=False, SolAngCor=True, paramFiles="- -" )
    return HBC.GetSumPixelArea( rpmt2, PixelRange )

