from __future__ import print_function

# "cos", "sin", "tan", "acos", "asin", "atan","cosh", "sinh", "tanh", "exp","log", "log10","sqrt","ceil", "fabs","floor","pow"
# "atan2", "frexp","ldexp", "modf", "fmod", "hypot"

import math

class eisf:

    #---------------------------------------------------------------

    def __init__(self):
#        self.func = "p0+(p1+sin(p3*(p3/p2)+(p6+p1)+p3))+3.14"
#        self.func = "p0+p1+(p3+p4)"
#        self.func = "sin(p0)+tan(p1)"
#        self.func = "-p0/p1"
        self.q = 0
#        self.name =["p0","p1","p2","p3","p4","p5","p6"]
#        self.p    =["1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ]
#        self.p    =[
#                           ["1.0","1.1","1.2"],
#                           ["2.0","2.2","2.4"],
#                           ["3.0","3.3","3.6"],
#                           ["4.0","4.4","4.8"],
#                           ["5.0","5.5","6.0"],
#                           ["6.0","6.6","7.2"],
#                           ["7.0","7.7","8.4"]
#                          ]


    #---------------------------------------------------------------

    def divideListElem(self,divval,k):

        if k==0:
            self.ansdivval=float(self.substitute(divval[0]))
        else:
            self.ansdivval/=float(self.substitute(divval[k]))

    #---------------------------------------------------------------

    def multiplyListElem(self,multiplyval,j):

        if j==0:
            self.ansmultiplyval=float(self.substitute(multiplyval[0]))
        else:
            self.ansmultiplyval*=float(self.substitute(multiplyval[j]))

    #---------------------------------------------------------------

    def divideListElemErr(self,divval,divvale,k):
        if k==0:
            self.ansdivvalErr=float(self.substituteErr(divvale[0]))
        else:
            self.ansdivvalErr=math.sqrt((math.pow(float(self.substituteErr(divvale[k]))/float(self.substitute(divval[k])),2)\
                                    +math.pow((self.ansdivvalErr/self.ansdivval),2)))\
                                    *self.ansdivval/float(self.substitute(divval[k]))

    #---------------------------------------------------------------

    def multiplyListElemErr(self,multiplyval,multiplyvale,j):
    
        if j==0:
            self.ansmultiplyvalErr=float(self.substituteErr(multiplyvale[0]))
        else:
            self.ansmultiplyvalErr= math.sqrt((math.pow(float(self.substituteErr(multiplyvale[j]))/float(self.substitute(multiplyval[j])),2)\
                                    +math.pow((self.ansmultiplyvalErr/self.ansmultiplyval),2)))\
                                    *self.ansmultiplyval*float(self.substitute(multiplyval[j]))
    #---------------------------------------------------------------

    def substractListElem(self,subval,i):

        if i==0:
            self.anssubval=float(self.substitute(subval[0]))
        else:
            self.anssubval-=float(self.substitute(subval[i]))

    #---------------------------------------------------------------

    def addListElem(self,addval,l):

        if l==0:
            self.ansaddval=float(self.substitute(addval[0]))
        else:
            self.ansaddval+=float(self.substitute(addval[l]))

    #---------------------------------------------------------------

    def substractListElemErr(self,subvale,i):
        print(i,subvale)
        if i==0:
            self.anssubvalErr=float(self.substituteErr(subvale[0]))
        else:
            self.anssubvalErr= math.sqrt(math.pow(self.anssubvalErr,2)+ math.pow(float(self.substituteErr(subvale[i])),2))

    #---------------------------------------------------------------

    def addListElemErr(self,addvale,l):

        if l==0:
            self.ansaddvalErr = float(self.substituteErr(addvale[0]))
        else:
            self.ansaddvalErr = math.sqrt(math.pow(self.ansaddvalErr,2)+ math.pow(float(self.substituteErr(addvale[l])),2))


    #---------------------------------------------------------------

    def substitute(self,val):
        #print "---a",val
        if "p" in val:
            index=val.split("p")[1]
            #print "p"
            return self.p[int(index)-1][self.q]
        elif "x" in val or "Q" in val:
            #print "---",self.p[-1][self.q]
            return self.p[-1][self.q]
        else:
            #print "else"
            return val

    #---------------------------------------------------------------

    def substituteErr(self,val):
        #print "---a",val
        if "p" in val:
            index=val.split("p")[1]
            #print "p"
            return self.e[int(index)-1][self.q]
        elif "x" in val or "Q" in val:
            #print "---",self.p[-1][self.q]
            return self.e[-1][self.q]
        else:
            #print "else"
            return val

    #---------------------------------------------------------------

    def arithmeticOperation(self,inputParam):
        print("D-",inputParam)
        if not inputParam.replace(".","").replace("p","").replace("x","1").replace("Q","1").replace("|","").isdigit():
            if "|" in inputParam:
                addval=(inputParam.split("|"))[0].split("+")
                addvalErr=(inputParam.split("|"))[1].split("+")
            else:
                addval=inputParam.split("+")
                addvalErr=inputParam.split("+")

            for l in range(len(addval)): #==> + start addition term
                if not addval[l].replace(".","").replace("p","").replace("x","1").replace("Q","1").replace("e+","").replace("e-","").isdigit():
                    subval=addval[l].replace("e+","EP").replace("e-","EM").split("-")
                    subvalErr=addvalErr[l].replace("e+","EP").replace("e-","EM").split("-")

                    if subval[0]=="" and subvalErr[0]!="":
                        subval[0]="0"
                        subvalErr.insert(0,"0")
                    if subvalErr[0]=="" and subval[0]!="":
                        subval.insert(0,"0")
                        subvalErr[0]="0"
                    if subvalErr[0]=="" and subval[0]=="":
                        subval[0]="0"
                        subvalErr[0]="0"

                    subval=[subvalsp.replace("EP","e+").replace("EM","e-") for subvalsp in subval]
                    subvalErr=[subvalErrsp.replace("EP","e+").replace("EM","e-") for subvalErrsp in subvalErr]

                    #print "$$$$$",subval
                    for i in range(len(subval)): #==> - start substraction term
                        if not self.rep(subval[i]).isdigit():
                            multiplyval=subval[i].split("*")
                            multiplyvalErr=subvalErr[i].split("*")
                            for j in range(len(multiplyval)): #==> * start nultiplication term
                                if not self.rep(multiplyval[j]).isdigit():
                                    divval=multiplyval[j].split("/")
                                    divvalErr=multiplyvalErr[j].split("/")

                                    for k in range(len(divval)): #==> / start division term
                                        if not self.rep(divval[k]).replace("-","").isdigit():
                                            self.ansdivval=0
                                            self.ansdivvalErr=0

                                        if self.rep(divval[k]).isdigit():
                                            self.divideListElemErr(divval,divvalErr,k)
                                            self.divideListElem(divval,k)

                                            #print "$$$$",divval
                                    #multiplyval[j]=str(self.ansdivval)  #==> / end
                                    st=self.ansdivval
                                    multiplyval[j]= '%(st).16f' % vars()
                                    ste=self.ansdivvalErr
                                    multiplyvalErr[j]= '%(ste).16f' % vars()
                                if self.rep(multiplyval[j]).isdigit():
                                    self.multiplyListElemErr(multiplyval,multiplyvalErr,j)
                                    self.multiplyListElem(multiplyval,j)
                                    #print "$$$",multiplyval
                            #subval[i] = str(self.ansmultiplyval) #==> * end
                            st = self.ansmultiplyval
                            subval[i] = '%(st).16f' % vars()
                            ste = self.ansmultiplyvalErr
                            subvalErr[i] = '%(ste).16f' % vars()
                        if self.rep(subval[i]).isdigit():
                            self.substractListElem(subval,i)
                            self.substractListElemErr(subvalErr,i)
                            #print "$$",subval
                    #addval[l] = str(self.anssubval) #==> - end
                    st=self.anssubval
                    addval[l] = '%(st).16f' % vars()
                    ste=self.anssubvalErr
                    addvalErr[l] = '%(ste).16f' % vars()

                if self.rep(addval[l]).isdigit():
                    self.addListElem(addval,l)
                    self.addListElemErr(addvalErr,l)

                    #print "$",addval
            #ansval = str(self.ansaddval) #==> + end
            #print "###",ansval
            st=self.ansaddval
            ansval = '%(st).16f' % vars() #==> + end
            ste=self.ansaddvalErr
            ansvalErr = '%(ste).16f' % vars() #==> + end

            #print "###",ansval,st,inputParam
        else:
            if "|" in inputParam:
                ansval=self.substitute(inputParam.split("|")[0])
                ansvalErr=self.substituteErr(inputParam.split("|")[1])
            else:
                ansval=self.substitute(inputParam)
                ansvalErr=self.substituteErr(inputParam)

            #print "###",ansval,"---",inputParam
        print("A-",ansval,ansvalErr)
        return ansval,ansvalErr

    def rep(self,text):

        text = text.replace(".","")
        text = text.replace("p","")
        text = text.replace("x","1")
        text = text.replace("Q","1")
        text = text.replace("e+","")
        text = text.replace("e-","")
        text = text.replace("-","")

        return text

    #---------------------------------------------------------------

    def connection(self,paramlist):
        result=""
        for i in range(len(paramlist)):
            result+=str(paramlist[i])

        return result

    #---------------------------------------------------------------

    def checkFuncNamePos(self,func,param):
        return param.find(func)==len(param)-len(func) and param.find(func)>=0

    #---------------------------------------------------------------

    def associativeAction(self,paramlist):
        #print "---c",paramlist
        subcoefficient=0
        if len(paramlist)!=3:
            return self.connection(paramlist) 
        else:
            if paramlist[1][1:-1].find(",")==-1:
                afterPartialCalc,afterPartialCalcErr=self.arithmeticOperation(paramlist[1][1:-1])
            else:
                afterPartialCalc,afterPartialCalcErr=self.arithmeticOperation(paramlist[1][1:-1].split(",")[0])
                subcoefficient=float(paramlist[1][1:-1].split(",")[1])
            # sinh
            if self.checkFuncNamePos("sinh",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("sinh")]
                val2=str(math.sinh(float(afterPartialCalc)))
                val3=str(math.sinh(float(afterPartialCalcErr)))
            # cosh
            elif self.checkFuncNamePos("cosh",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("cosh")]
                val2=str(math.cosh(float(afterPartialCalc)))
                val3=str(math.cosh(float(afterPartialCalcErr)))
            # tanh
            elif self.checkFuncNamePos("tanh",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("tanh")]
                val2=str(math.tanh(float(afterPartialCalc)))
                val3=str(math.tanh(float(afterPartialCalcErr)))
            # sin
            elif self.checkFuncNamePos("sin",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("sin")]
                val2=str(math.sin(float(afterPartialCalc)))
                val3=str(math.sin(float(afterPartialCalcErr)))
            # cos
            elif self.checkFuncNamePos("cos",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("cos")]
                val2=str(math.cos(float(afterPartialCalc)))
                val3=str(math.cos(float(afterPartialCalcErr)))
            # tan
            elif self.checkFuncNamePos("tan",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("tan")]
                val2=str(math.tan(float(afterPartialCalc)))
                val3=str(math.tan(float(afterPartialCalcErr)))
            # asin
            elif self.checkFuncNamePos("asin",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("asin")]
                if float(afterPartialCalc)>1:
                    print("x in asin(x) needs to be lower than 1.")
                    val2="0"
                    val3="0"
                else:
                    val2=str(math.asin(float(afterPartialCalc)))
                    val3=str(math.asin(float(afterPartialCalcErr)))
            # acos
            elif self.checkFuncNamePos("acos",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("acos")]
                if float(afterPartialCalc)>1:
                    print("x in acos(x) needs to be lower than 1.")
                    val2="0"
                    val3="0"
                else:
                    val2=str(math.acos(float(afterPartialCalc)))
                    val3=str(math.acos(float(afterPartialCalcErr)))
            # atan
            elif self.checkFuncNamePos("atan",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("atan")]
                val2=str(math.atan(float(afterPartialCalc)))
                val3=str(math.atan(float(afterPartialCalcErr)))
            # exp
            elif self.checkFuncNamePos("exp",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("exp")]
                val2=str(math.exp(float(afterPartialCalc)))
                val3=str(math.exp(float(afterPartialCalcErr)))
            # log10
            elif self.checkFuncNamePos("log10",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("log10")]
                if float(afterPartialCalc)<-1:
                    print("x in log10(x) needs to be larger than -1.")
                    val2="0"
                    val3="0"
                else:
                    val2=str(math.log10(float(afterPartialCalc)))
                    val3=str(math.log10(float(afterPartialCalcErr)))
            # log
            elif self.checkFuncNamePos("log",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("log")]
                if float(afterPartialCalc)<-1:
                    print("x in log(x) needs to be larger than -1.")
                    val2="0"
                    val3="0"
                else:
                    val2=str(math.log(float(afterPartialCalc)))
                    val3=str(math.log(float(afterPartialCalcErr)))
            # sqrt
            elif self.checkFuncNamePos("sqrt",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("sqrt")]
                if float(afterPartialCalc)<0:
                    print("x in sqrt(x) needs to be larger than -1.")
                    val2="0"
                    val3="0"
                else:
                    val2=str(math.sqrt(float(afterPartialCalc)))
                    val3=str(math.sqrt(float(afterPartialCalcErr)))
            # ceil
            elif self.checkFuncNamePos("ceil",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("ceil")]
                val2=str(math.ceil(float(afterPartialCalc)))
                val3=str(math.ceil(float(afterPartialCalcErr)))
            # fabs
            elif self.checkFuncNamePos("fabs",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("fabs")]
                val2=str(math.fabs(float(afterPartialCalc)))
                val3=str(math.fabs(float(afterPartialCalcErr)))
            # floor
            elif self.checkFuncNamePos("floor",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("floor")]
                val2=str(math.floor(float(afterPartialCalc)))
                val3=str(math.floor(float(afterPartialCalcErr)))
            # pow
            elif self.checkFuncNamePos("pow",paramlist[0]):
                val1=paramlist[0][:paramlist[0].find("pow")]
                val2=str(math.pow(float(afterPartialCalc),subcoefficient))
                val3=str(math.pow(float(afterPartialCalcErr),subcoefficient))
            else:
                val1=paramlist[0]
                val2=afterPartialCalc
                val3=afterPartialCalcErr
            return self.connection([val1,val2,paramlist[2]])+"|"+self.connection([val1,val3,paramlist[2]])

    #---------------------------------------------------------------

    def main_loop(self,param):

        r = param.find(")")
        l = param.rfind("(",0,r)+1

        if r==-1 and l==0:
            sp = ["","("+param+")",""]
        else:
            sp = [param[0:l-1], param[l-1:r+1], param[r+1:]]
        return self.associativeAction(sp)

    #---------------------------------------------------------------

    def main(self,initialFunc,param):
        #print param
        self.func=initialFunc
        self.p=param
        result =[]
        for i in range(len(self.p[0])):
            while -1:
                self.func = self.main_loop(self.func)
                   
                if self.func.find(")")==-1 and (self.func.find("-")<=0):
                    afterPartialCalc=self.arithmeticOperation(self.func)
                    break
            result.append(float(afterPartialCalc))
            self.func=initialFunc
            #print afterPartialCalc,"-- end"
            self.q+=1

        return result
    #---------------------------------------------------------------

    def mainWithError(self,initialFunc,param,paramErr):
        #print param
        self.func=initialFunc
        self.funce=initialFunc
        self.p=param
        self.e=paramErr
        result =[]
        resultErr =[]
        for i in range(len(self.p[0])):
            while -1:
                self.func = self.main_loop(self.func)
                   
                if self.func.find(")")==-1 and (self.func.find("-")<=0):
                    afterPartialCalc,afterPartialCalcErr=self.arithmeticOperation(self.func)
                    print("B-",afterPartialCalc,afterPartialCalcErr)
                    break
            result.append(float(afterPartialCalc))
            resultErr.append(float(afterPartialCalcErr))
            self.func=initialFunc
            #print afterPartialCalc,"-- end"
            self.q+=1

        return result,resultErr
    #---------------------------------------------------------------


if __name__ == "__main__":
    func = "p1*p1+p2"
    #func = "pow(p1,2)+p2"
    print("Function >> ",func)

    param    = [
                        ["1.0","1.1","1.2"],
                        ["2.0","2.2","2.4"],
                        ["3.0","3.3","3.6"],
                        ["4.0","4.4","4.8"],
                        ["5.0","5.5","6.0"],
                        ["6.0","6.6","7.2"],
                        ["7.0","7.7","8.4"]
                        ]

    app = eisf()
    #app.arithmeticOperation()
    result=app.main(func,param)

    print(result, "<-- result")
