#!/usr/bin/python

from math import *
import numpy as np
import Manyo as m
#import BSpline as Adv
#import Adv
Adv = m.BSpline()


####    linear combination of gaussian    ####
#def dist(x, n, a, c, w):
#def dist(x, background, param):
def dist(x, param):
    sum=param[0]
    for i in range(len(param[1])):
        a, c, w = param[1][i]
        sum = sum + a*exp(-log(2.0)*((x-c)/w)**2)
    return sum

####    sign function    ####
def sign(r):
    if r > 0.0:
        s = 1.0
    elif r == 0.0:
        s = 0.0
    else:
        s = -1.0
    return s

####    noise function    ####
def noise(p, ratio):
    return sign(np.random.rand(1)-0.5)*p*ratio*sqrt(-1.0*log(np.random.rand(1)))

####    initialize an ElementContainer    ####
#def initElementContainer(xmin, xmax, nDiv, background, param):
def initElementContainer(xmin, xmax, nDiv, param):

    bin = m.MakeDoubleVector(nDiv+1)
    delta = (xmax - xmin)/nDiv
    for i in range(nDiv+1):
        bin[i] = xmin + delta*i

    y = m.MakeDoubleVector(nDiv)
    e = m.MakeDoubleVector(nDiv)
    ratio=0.1
    for i in range(nDiv):
        xc = (bin[i] + bin[i+1])/2.0
        #p = dist(xc, background, param)
        p = dist(xc, param)
        q = noise(p, ratio)
        y[i] = p + q
        e[i] = p*ratio

    ec = m.ElementContainer()
    ec.AddToHeader("run number", 1)
    ec.AddToHeader("level",      1)
    ec.AddToHeader("Inst.",      "manyo")
    ec.Add("TOF",       bin, "sec." )
    ec.Add("Intensity", y,   "count")
    ec.Add("Error",     e,   "count")
    ec.SetKeys("TOF", "Intensity", "Error")

    return ec

####    output an element container    ####
def outputElementContainer(ec):
    bin = ec.Put(ec.PutXKey())
    y   = ec.Put(ec.PutYKey())
    e   = ec.Put(ec.PutEKey())

    print str("No.").rjust(4), str("-------- bin --------").rjust(21), str("xc").rjust(7), ec.PutYKey().rjust(23), ec.PutEKey().rjust(23)
    for i in range(ec.PutSize(ec.PutYKey())):
         print str(i).rjust(4), "[", str(bin[i]).rjust(7), ",", str(bin[i+1]).rjust(7), ")", str((bin[i]+bin[i+1])/2.0).rjust(7), str(y[i]).rjust(23), str(e[i]).rjust(23)
    print ""

####    output a domain    ####
def outputDomain(domain):
    print "bound                 [", str(domain.getLowerBound()     ).rjust(5), ",", str(domain.getUpperBound()     ).rjust(5), "]"
    print "bin bound id          [", str(domain.getLowerBinBoundID()).rjust(5), ",", str(domain.getUpperBinBoundID()).rjust(5), "]"
    print "bin id                [", str(domain.getLowerBinID()     ).rjust(5), ",", str(domain.getUpperBinID()     ).rjust(5), "]"
    print "bound id (deprecated) [", str(domain.getLowerBoundID()   ).rjust(5), ",", str(domain.getUpperBoundID()   ).rjust(5), "]"
    print
    print "type of the domain", domain.getType()

####    plot    ####
def plot(src, dest):
    srcBin  = src.Put(src.PutXKey())
    srcY    = src.Put(src.PutYKey())
    destBin = dest.Put(dest.PutXKey())
    destY   = dest.Put(dest.PutYKey())

    count=0
    g=m.GnuplotInterface()
    g.e("set xrange[0: 15]")
    g.e("set yrange[0: 150000]")
    g.e("plot '-' using 1:2 with lines linewidth 1 title'%d', '-' using 1:3 with lines linewidth 4" %count)
    while 1:
        for i in range(srcY.size()):
            #print str( (srcBin[i]+srcBin[i+1])/2.0 ) + " " + str( srcY[i] )+ " " + str( destY[i] )
            g.e( str( (srcBin[i]+srcBin[i+1])/2.0 ) + " " + str( srcY[i] )+ " " + str( destY[i] ) )
        g.e("e")
        c=raw_input("")
        if c != "":
            break

############            main routine            ############
####    initializse the element container    ####
xmin = 0.0
xmax = 15.0
nDiv = 1500

#      background    param for f0          param for f1
param1=[1000.0,     [[100000.0, 5.0, 2.5], [100000.0, 10.0, 2.5] ] ]

#src = initElementContainer(xmin, xmax, nDiv, background1, param1)
src = initElementContainer(xmin, xmax, nDiv, param1)
outputElementContainer(src)
#src1 = initElementContainer(xmin, xmax, nDiv)
#outputElementContainer(src1)
#src1.Dump()

####    initializse the domain    ####
domain = m.Domain()
domain.setSource(src)
domain.setRange(xmin, xmax)
outputDomain(domain)
#domain.setType(Domain.OO)
#print "type of the domain", domain.getType()


####    initializse the parameters for B-spline fitting    ####
param = m.ParamSet()
param.add(m.BSpline.ORDER,                    3     )  # the order of B spline polynominals
param.add(m.BSpline.AUTOMATIC_KNOTS,          False )  # automatic assignment of knots
param.add(m.BSpline.USE_UNIFORM_BREAK_POINTS, True  )  # assignment of uniform break points in the user-defined domain
param.add(m.BSpline.NUMBER_OF_BREAK_POINTS,   10    )  # the number of break points
param.dump()

print "issubclass(Adv.BSpline, Adv.Method )", issubclass(m.BSpline, m.Method )
print "issubclass(Adv.Method,  Adv.BSpline)", issubclass(m.Method,  m.BSpline)
method = m.BSpline()
print "mathod name      : ", method.getMethodName()
print "is differentiable: ", method.differentiable()

param = method.setDefaultParam(src)
method.setDefaultParam(src).dump()

print "check param: ", method.checkParam(src, domain, param)
if method.checkParam(src, domain, param):
    method.toInnerForm(src, domain, param)
    method.fit()
    method.eval()
    result = m.ElementContainer(src.PutHeader())
    method.toElementContainer(src, result)
    #result.dump()
    outputElementContainer(result)
    plot(src, result)
