#!/usr/local/bin/Rexec
########### set arguments list ###############################
args<-commandArgs();
args_start=grep("--args",args)+1;
if  (length(args_start)>0 && args_start < length(args) ) {
    args<-args[args_start:length(args)]
} else {
    args=c()
}

onset<-function(
                width=4,
                height=6,
                eps=FALSE,
                dt=.001,
                time=10/dt,
                tau1=1,tau2=tau1*2/3,
                k=1,
                start1=ceiling(time/8),
                start2=ceiling(time*2/8)) {

    v = matrix(rep(0,time*2),time,2);
    u = matrix(rep(0,time*2),time,2);
    h = matrix(rep(0,time*2),time,2);
    h[start1,1]=1000;
    h[start2,2]=1000;
    xgap=time/5;
    xlim=c(-xgap,time+xgap)
    ylim=c(-.5,1.5)
    axlim=c(-xgap/2,time)
    aylim=c(-.3,1.3)

    lwd <- 1.5

    # simulation
    for(t in 2:time) {

        # pre-synaptic onset
        v[t,1] = v[t-1,1] + (dt/tau1)*( -v[t-1,1] +k*h[t,1] );
        u[t,1] = u[t-1,1] + (dt/tau1)*( -u[t-1,1] +v[t-1,1] );

        # post-synaptic onset
        v[t,2] = v[t-1,2] + (dt/tau2)*( -v[t-1,2] +k*h[t,2] );
        u[t,2] = u[t-1,2] + (dt/tau2)*( -u[t-1,2] +v[t-1,2] );

    }
        if(eps==TRUE) {
        postscript(
                   'onset.eps',
                   onefile=FALSE,
                   horizontal=FALSE,
                   width=width,height=height,
                   family="times");
    }

    layout(c(4,1,1,1,2,2,2,3,3,3,4),11,1)
    par(mar=c(.01,.01,.01,.01))

    plot(
         h[,1]/1000,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=lwd,
         xlim=xlim,
         ylim=ylim,
         family="serif")

    polygon(c(1:time,time), c(h[,1], h[,1][1]), col='#DDDDDD',border=NA)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=lwd,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(h(t))),cex=2)
    text(start1,-.3,expression(italic(t[i])),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)

    plot(
         1e200,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=1,
         xlim=xlim,
         ylim=ylim)

    polygon(c(1:time,time), c(v[,1], v[,1][1]), col='#DDDDDD',border=NA)
    lines(v[,1], lwd=lwd)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=lwd,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(v(t))),cex=2)
    text(start1,-.3,expression(italic(t[i])),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)

    plot(
         1e200,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=1,
         xlim=xlim,
         ylim=ylim)

    polygon(c(1:time,time), c(u[,1], u[,1][1]), col='#DDDDDD',border=NA)
    lines(u[,1], lwd=lwd)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=lwd,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(u(t))),cex=2)
    text(start1,-.3,expression(italic(t[i])),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(start1+ceiling(tau1/dt),-.3,expression(italic(tau+t[i])),cex=2,adj=c(.2,.5))
    arrows(start1+ceiling(tau1/dt),0,start1+ceiling(tau1/dt),
           u[start1+ceiling(tau1/dt),1],lwd=lwd,length=0,lty=3);

    if(eps==TRUE) {
        graphics.off();
    }

}

derivatives<-function(
                      width=4,
                      height=6,
                      eps=FALSE,
                      name="onset_tau.eps",
                      dt=.001,
                      time=10/dt,
                      tau1=1,tau2=tau1,
                      k=1,
                      start1=ceiling(dt)+1,
                      start2=ceiling(time*2/8)) {

    v = matrix(rep(0,time*2),time,2);
    u = matrix(rep(0,time*2),time,2);
    h = matrix(rep(0,time*2),time,2);
    h[start1:length(h[,1]),1]=1;
    h[start2:length(h[,2]),2]=1;
    xgap=time/5;
    xlim=c(-xgap,time+xgap)
    ylim=c(-.5,1.3)
    axlim=c(-xgap/2,time)
    aylim=c(-.3,1.1)

    # simulation
    for(t in 2:time) {

        # pre-synaptic onset
        v[t,1] = v[t-1,1] + (dt/tau1)*( -v[t-1,1] +k*h[t,1] );
        u[t,1] = u[t-1,1] + (dt/tau1)*( -u[t-1,1] -v[t-1,1] + k*h[t,1] );

        # post-synaptic onset
        v[t,2] = v[t-1,2] + (dt/tau2)*( -v[t-1,2] +k*h[t,2] );
        u[t,2] = u[t-1,2] + (dt/tau2)*( -u[t-1,2] -v[t-1,2] + k*h[t,2] );

    }
        u1=u[,1];
    u2=u[,2];
    timetime=1:time;
    du1=c(0,(u1[2:time]-u1[1:(time-1)])/dt);
    du2=c(0,(u2[2:time]-u2[1:(time-1)])/dt);
    du1p=du1*(du1>0);
    du1n=abs(du1*(du1<0));
    du2p=du2*(du2>0);
    du2n=abs(du2*(du2<0));

    if(eps==TRUE) {
        postscript(
                   name,
                   onefile=FALSE,
                   horizontal=FALSE,
                   width=width,height=height,
                   family="Times");
    }

    layout(seq(1,4,1),4,1)
    par(mar=c(.01,.01,.01,.01))

    plot(
         1e100,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=1,
         xlim=xlim,
         ylim=ylim,
         family="serif")

    polygon(c(1:time,time), c(u1, u1[1]), col='#DDDDDD',border=NA)
    lines(u1,lwd=2);
    polygon(c(1:time,time), c(u2, u2[1]), col='#EEEEEE',border=NA)
    lines(u2,lwd=1);
    #lines(u1,lwd=0.5);
    arrows(4,-.1,start2-start1-4,-.1,code=3,angle=90,length=.02,lwd=.6)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(u(t))),cex=2)
    text(time*(1/16),-.3,expression(paste(Delta,"t")),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(timetime[u1==max(u1)][1],max(u1)+.2,expression(italic(u[1])),cex=2)
    text(timetime[u2==max(u2)][1],max(u2)+.2,expression(italic(u[2])),cex=2)

    plot(
         1e200,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=3,
         xlim=xlim,
         ylim=ylim,
         family="serif")

    polygon(c(1:time,time), c(du1, du1[1]), col='#DDDDDD',border=NA)
    lines(du1,lwd=2);
    polygon(c(1:time,time), c(du2, du2[1]), col='#EEEEEE',border=NA)
    lines(du2,lwd=1);
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(dot(u)(t))),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(timetime[du1==max(du1)]+1000,max(du1),expression(italic(dot(u)[1])),cex=2)
    text(timetime[du2==max(du2)]+1000,max(du2),expression(italic(dot(u)[2])),cex=2)
    taux = ceiling(tau1/dt);
    tau2x = (start2-start1)+ceiling(tau2/dt);

    plot(
         1e200,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=3,
         xlim=xlim,
         ylim=ylim,
         family="serif")

    polygon(c(1:time,time), c(du1p, du1p[1]), col='#DDDDDD',border=NA)
    lines(du1p,lwd=2);
    polygon(c(1:time,time), c(du2p, du2p[1]), col='#EEEEEE',border=NA)
    lines(du2p,lwd=1);
    points(c(taux,tau2x),c(0,0),pch=19)

    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(dot(u)(t))),cex=2)
    text(taux,-.3,expression(italic(tau[1])),cex=2)
    text(tau2x,-.3,expression(italic(tau[2] + paste(Delta,'t'))),adj=c(0.1,.4),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(timetime[du1p==max(du1p)][1]+1400,max(du1p),expression(group("[",italic(dot(u)[1]),"]")^{'+'}),cex=2)
    text(timetime[du2p==max(du2p)][1]+1400,max(du2p),expression(group("[",italic(dot(u)[2]),"]")^{'+'}),cex=2)

    plot(
         1e200,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=3,
         xlim=xlim,
         ylim=ylim,
         family="serif")

    polygon(c(1:time,time), c(du1n, du1n[1]), col='#DDDDDD',border=NA)
    lines(du1n,lwd=2);
    polygon(c(1:time,time), c(du2n, du2n[1]), col='#EEEEEE',border=NA)
    lines(du2n,lwd=1);
    points(c(taux,tau2x),c(0,0),pch=19)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(dot(u)(t))),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(taux,-.3,expression(italic(tau[1])),cex=2)
    text(tau2x,-.3,expression(italic(tau[2] + paste(Delta,'t'))),adj=c(0.1,.4),cex=2)
    text(timetime[du1n==max(du1n)][1][1]-40,max(du1n)+.3,expression(group("[",italic(dot(u)[1]),"]")^{'-'}),cex=2)
    text(timetime[du2n==max(du2n)][1][1],max(du2n)+.3,expression(group("[",italic(dot(u)[2]),"]")^{'-'}),cex=2)

    if(eps==TRUE) {
        graphics.off();
    }

}

hsides<-function(
                 width=4,
                 height=6,
                 ylim=c(-.3,1.5),
                 eps=FALSE,
                 name="hd",
                 dt=.01,
                 time=10/dt,
                 start1=ceiling(time/8),
                 start2=ceiling(time*2/8),
                 co1=1,
                 co2=1)
{

    # init variable s of simulation

    h = matrix(rep(0,time*2),time,2);
    h[start1:length(h[,1]),1]=1;
    h[start2:length(h[,2]),2]=1;

    # define constants for graph limits
    xgap=time/4;
    xlim=c(-xgap*2/3,time+xgap);
    aylim=ylim
    ylimrange= ylim[2]-ylim[1]
    ylim=ylim+c(-ylimrange/20, +ylimrange/5);
    axlim=c(-xgap/2,time);
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))

    # define constants for text positions
    maxlabx=1/4; maxlaby=8/18;
    maxlabx0=-1/12; maxlabx1=-1/12; maxlabx2=-1/12; maxlabx3=-1/12; maxlabx4=-1/12;
    maxlaby0=2/18; maxlaby1=7/18; maxlaby2=10/18; maxlaby3=13/18; maxlaby4=16/18;
    lpx=c(0,1,1,0);  lpy=c(1,1,0,0); bl=.1*time;
    pgap = ylimrange/2; right=.12*time;

    timetime=1:time;

    hd=(h[2:dim(h)[1],]-h[1:(dim(h)[1]-1),]);

    if(eps==TRUE) {
        postscript(
                   paste('onset_',name,'.eps',sep=''),
                   onefile=FALSE,
                   horizontal=FALSE,
                   pointsize=6,
                   width=width,height=height,family="Times");
    }

    par(mar=c(.01,.01,.01,.01))
    plot(
         hd[,2],
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=1,
         col='#ffffff',
         xlim=xlim,
         ylim=ylim,
         family="serif");

    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))

    lines(hd[,1],lwd=2)
    lines(hd[,2],lwd=2)
    hd1=hd[,1]
    hd2=hd[,2]
    text(-xgap*3/6,aylim[2],expression(italic(s(t))),cex=2)

    text(timetime[hd1==max(hd1)][1], max(hd1)+.5*pgap,expression(italic(s[1])),cex=2)
    text(timetime[hd2==max(hd2)][1]+40*pgap, max(hd2)+.5*pgap,expression(italic(s[2])),cex=2)
    text(time+time*0.04,-.2*pgap,expression(italic(t)),cex=2)

    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);

    if(eps==TRUE) {
        graphics.off();
    }
}

componentEvents<-function(
                        width=4, # width of eps figure (inches)
                        height=6, # height of eps figure (inches)
                        ylim=c(-.3,1.5), # range of amplitude shown in the graph
                        eps=FALSE, #  print eps figure intead of screen
                        name="wpp1", # print eps figure intead of screen
                        dt=.01, # integration step
                        time=10/dt, # timesteps of the simulated trial
                        tau1=1, # decay of the pre-synaptic neuron
                        tau2=tau1, # decay of the post-synaptic neuron
                        k=1, # amplification of input
                        start1=ceiling(time/8), # start of the signal to the pre-synaptic neuron
                        start2=ceiling(time*2/8), # start of the signal to the post-synaptic neuron
                        co1=1, # type of the pre-synaptic event (can be
                               # 0:onset,
                               # 1:positive part of derivative of onset,
                               # 2: absolute of negative derivative of onset)
                        co2=1,  # type of the post-synaptic event (can be
                               # 0:onset,
                               # 1:positive part of derivative of onset,
                               # 2: absolute of negative derivative of onset)
                        plot_prod=FALSE
                        ) {

    # init variable s of simulation

    v = matrix(rep(0,time*2),time,2);
    u = matrix(rep(0,time*2),time,2);
    h = matrix(rep(0,time*2),time,2);
    h[start1:length(h[,1]),1]=1;
    h[start2:length(h[,2]),2]=1;

    # define constants for graph limits
    xgap=time/4;
    xlim=c(-xgap*2/3,time+xgap);
    aylim=ylim
    ylimrange= ylim[2]-ylim[1]
    ylim=ylim+c(-ylimrange/20, +ylimrange/20);
    axlim=c(-xgap/2,time);
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))

    # define constants for text positions
    maxlabx=1/4; maxlaby=8/18;
    maxlabx0=-1/12; maxlabx1=-1/12; maxlabx2=-1/12; maxlabx3=-1/12; maxlabx4=-1/12;
    maxlaby0=2/18; maxlaby1=7/18; maxlaby2=10/18; maxlaby3=13/18; maxlaby4=16/18;
    lpx=c(0,1,1,0);  lpy=c(1,1,0,0); bl=.1*time;
    pgap = ylimrange/2; right=.12*time;

    # simulation
    for(t in 2:time) {

        # pre-synaptic onset
        v[t,1] = v[t-1,1] + (dt/tau1)*( -v[t-1,1] +k*h[t,1] );
        u[t,1] = u[t-1,1] + (dt/tau1)*( -u[t-1,1] -v[t-1,1] + k*h[t,1] );

        # post-synaptic onset
        v[t,2] = v[t-1,2] + (dt/tau2)*( -v[t-1,2] +k*h[t,2] );
        u[t,2] = u[t-1,2] + (dt/tau2)*( -u[t-1,2] -v[t-1,2] + k*h[t,2] );

    }

    # standardization of onsets
    u1=u[,1]; u1=u1/max(u1)
    u2=u[,2]; u2=u2/max(u2)

    # derivatives of onsets
    timetime=1:time;
    du1=c(0,(u1[2:time]-u1[1:(time-1)])/dt);du1=du1/max(du1)
    du2=c(0,(u2[2:time]-u2[1:(time-1)])/dt);du2=du2/max(du2)

    # positive and negative parts of derivatives
    du1p=du1*(du1>0);
    du1n=abs(du1*(du1<0));
    du2p=du2*(du2>0);
    du2n=abs(du2*(du2<0));
    dd=t(rbind(du1p,du1n,du2p,du2n));

    # decide if d1 and d2 events will be onsets
    # or positive/negative derivative
    if(co1==0) {
        d1=u1;
        du1=u1;
        d2=dd[,2+co2];
    } else if(co2==0) {
        d2=u2;
        du2=u2;
        d1=dd[,co1];
    } else {
        d2=dd[,2+co2];
        d1=dd[,co1];
    }

    # standardized combination on the two events
    ddd=d1*d2;
    if(max(ddd)!=0) {
        ddd=ddd/max(ddd)
        ddd=ddd*((max(d1)+max(d2))/2)
    }

    # decide if printing the graph on an eps file
    if(eps==TRUE) {
        postscript(
                   paste('onset_',name,'.eps',sep=''),
                   onefile=FALSE,
                   horizontal=FALSE,
                   width=width, height=height,
                   pointsize=6, family="Times");
    }

    #plot first event
    par(mar=c(.01,.01,.01,.01))
    plot(
         d1,
         type='l',
         frame.plot=FALSE,
         axes=FALSE,
         xlab='',
         ylab='',
         lwd=1,
         col='#ffffff',
         xlim=xlim,
         ylim=ylim,
         family="serif");

    #plot composition
    if(plot_prod) {
        polygon(c(1,1:time,time), c(0,ddd, 0), lwd=.01,col='#888888',border=NA)
    }
    #plot first event area
    polygon(c(1,1:time,time), c(0, d1,0), col='#444444',density=20,angle=-45,border=NA)
    #plot second event area
    polygon(c(1,1:time,time), c(0, d2,0), col='#444444',density=20,angle=45,border=NA)

    #plot first event
    lines(d1,lwd=2)
    #plot second event
    lines(d2,lwd=2)
    #plot axes
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    #plot axes'labels
    text(-xgap*3/6,aylim[2],expression(italic(u(t))),cex=2)
    text(time+time*0.04,-.1*pgap,expression(italic(t)),cex=2)

    # compose text to explain envent types in the legend
    if(co2==0) {
        if(co1==1) { first="group('[',dot(u)[1],']')^{'+'}" }
        else if (co1==2) {  first="group('[',dot(u)[1],']')^{'-'}" }
        second="u[2]"
    }else if(co1==0) {
        if(co2==1) { second="group('[',dot(u)[2],']')^{'+'}" }
        else if (co2==2) { second="group('[',dot(u)[2],']')^{'-'}" }
        first="u[1]"
    } else{
        if(co1==1) { first="group('[',dot(u)[1],']')^{'+'}" }
        else if (co1==2) { first="group('[',dot(u)[1],']')^{'-'}" }
        if(co2==1) { second="group('[',dot(u)[2],']')^{'+'}" }
        else if (co2==2) { second="group('[',dot(u)[2],']')^{'-'}" }
    }
    txt_prod = eval(parse(text=paste('expression(',first,'*',second,')')));
    txt_1 = eval(parse(text=paste('expression(',first,')')));
    txt_2 = eval(parse(text=paste('expression(',second,')')));

    #legend
    if(plot_prod) {
        text(time-time*maxlabx0,
             aylim[2]-aylim[2]*maxlaby0,
             txt_prod,cex=7,adj=c(.75,.5));
        if(max(ddd)!=0 )
        {
            polygon(time-time*maxlabx0+right+ceiling(lpx*bl),
                    (lpy*.1*pgap)+(aylim[2]-aylim[2]*maxlaby0-.05*pgap),
                    col='#888888', border=NA);
        }
    }
    else{
        text(time-time*(1/2),
             aylim[2],
             "component:",cex=2,adj=c(.75,.5));

        text(time-time*maxlabx1,
             aylim[2],
             txt_prod,cex=2,adj=c(.75,.5));
    }
    if(co1==1) {first='+'} else if (co1==2) {first='-'}
    text(time - time * maxlabx1, aylim[2] - aylim[2] * maxlaby1, txt_1, cex =
             2)

    polygon(
        time - time * maxlabx1 + right + ceiling(lpx * bl),
        (lpy * .2 * pgap) + (aylim[2] - aylim[2] * maxlaby1 - .05 *
                                 pgap)
        ,
        col = '#444444',
        density = 20,
        angle = -45,
        border = NA
    )

    polygon(
        time - time * maxlabx1 + right + ceiling(lpx * bl),
        (lpy * .2 * pgap) + (aylim[2] - aylim[2] * maxlaby1 - .05 *
                                 pgap)
        ,
        lwd = 2
    )

    if (co2 == 1) {
        first = '+'
    } else if (co2 == 2) {
        first = '-'
    }

    text(time - time * maxlabx2, aylim[2] - aylim[2] * maxlaby2, txt_2, cex =
             2)

    polygon(
        time - time * maxlabx2 + right + ceiling(lpx * bl),
        (lpy * .2 * pgap) + (aylim[2] - aylim[2] * maxlaby2 - .05 *
                                 pgap)
        ,
        col = '#444444',
        density = 20,
        angle = 45,
        border = NA
    )

    polygon(
        time - time * maxlabx2 + right + ceiling(lpx * bl),
        (lpy * .2 * pgap) + (aylim[2] - aylim[2] * maxlaby2 - .05 *
                                 pgap)
        ,
        lwd = 2
    )

    # print the eps file
    if(eps==TRUE) {
        dev.off()
    }

}

allcomponents <-function()  {

    labels1= c('wpp1','wpn1','wnp1','wnn1','wpp2','wpn2','wnp2','wnn2')
    labels2= c('wsp1','wsn1','wps1','wns1','wsp2','wsn2','wps2','wns2')

    tau1=1;
    tau2=1;
    start1=100;
    hh=1;
    l1 = 0; l2 = 0
    lab = 0
    for(start2 in c(50,150)) {
        hsides(eps=TRUE,dt=.01,start1=start1,start2=start2,
               name=paste("hd",hh,sep=''),width=2.5,height=0.5);
        hh=hh+1;
        for(co1 in c(0,1,2)) {
            for(co2 in c(0,1,2)) {
                if ((co1+co2)> 0) {
                    labels=labels1
                    if ( (co1 == 0 || co2 == 0) ) {
                        labels=labels2
                        l2 = l2 + 1
                        lab = l2
                    } else if ( co1 > 0 && co2 > 0 )  {
                        labels=labels1
                        l1 = l1 + 1
                        lab = l1
                    }

                    componentEvents(
                                    co1=co1,
                                    co2=co2,
                                    start1=start1,
                                    start2=start2,
                                    name=labels[lab],
                                    tau1=tau1,
                                    tau2=tau2,
                                    eps=TRUE,
                                    width=2.5,
                                    height=1.375);
                }
            }
        }
    }
}

onset_alpha<-function(
    width=4,
    height=6,
    eps=FALSE,
    dt=.001,
    time=10/dt,
    tau1=1,
    k=1,
    start1=ceiling(time/8)) {

    graphics.off();

    v = rep(0,time);
    u = rep(0,time);
    v = rep(0,time);
    h = rep(0,time);
    h[start1]=1;

    xgap=time/5;
    xlim=c(-xgap,time+xgap)
    ylim=c(-.5,1.5)
    axlim=c(-xgap/2,time)
    aylim=c(-.3,1.3)

    # simulation
    for(t in 2:time) {

        # pre-synaptic onset
        v[t] = v[t-1] + (dt/tau1)*( -v[t-1] +k*h[t] );
        u[t] = u[t-1] + (dt/tau1)*( -u[t-1] +v[t-1] );

    }
    if(eps==TRUE) {
        postscript(
            'onset.eps',
            onefile=FALSE,
            horizontal=FALSE,
            width=width,height=height,
            family="times");
    }

    layout(seq(1,3,1),3,1)
    par(mar=c(.01,.01,.01,.01))

    plot(
        h,
        type='l',
        frame.plot=FALSE,
        axes=FALSE,
        xlab='',
        ylab='',
        lwd=3,
        xlim=xlim,
        ylim=ylim,
        family="serif")

    polygon(c(1:time,time), c(h, h[1]), col='#DDDDDD',border=NA)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(h(t))),cex=2)
    text(start1,-.3,expression(italic(t[i])),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)
    text(time,-.3,expression(italic(t)),cex=2)

    ylim=c(-.0005,0.0015)
    aylim=c(-.0003,0.0013)
    plot(
        v,
        type='l',
        frame.plot=FALSE,
        axes=FALSE,
        xlab='',
        ylab='',
        lwd=3,
        xlim=xlim,
        ylim=ylim)

    polygon(c(1:time,time), c(v, v[1]), col='#DDDDDD',border=NA)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(v(t))),cex=2)
    text(start1,aylim[1],expression(italic(t[i])),cex=2)
    text(time,aylim[1],expression(italic(t)),cex=2)

    plot(
        u,
        type='l',
        frame.plot=FALSE,
        axes=FALSE,
        xlab='',
        ylab='',
        lwd=3,
        xlim=xlim,
        ylim=ylim)

    polygon(c(1:time,time), c(u, u[1]), col='#DDDDDD',border=NA)
    d<-c()
    d<-rbind(d,c(rbind(axlim,aylim*0)))
    d<-rbind(d,c(rbind(axlim*0,aylim)))
    arrows(d[,1],d[,2],d[,3],d[,4],lwd=1,length=.08);
    text(-xgap*2/3,aylim[2],expression(italic(u(t))),cex=2)
    text(start1,aylim[1],expression(italic(t[i])),cex=2)
    text(time,aylim[1],expression(italic(t)),cex=2)
    text(start1+ceiling(tau1/dt),aylim[1],expression(italic(tau+t[i])),cex=2,adj=c(.2,.5))
    arrows(start1+ceiling(tau1/dt),0,start1+ceiling(tau1/dt),u[start1+ceiling(tau1/dt),1],lwd=1,length=0,lty=3);

    if(eps==TRUE) {
        graphics.off();
    }

    v
}

case_study <- function() {
    ##### case study#
    x11()
    componentEvents(
        co1 = 1,
        co2 = 0,
        start1 = 20,    start2 = 16,
        dt = 0.001,
        time = 80,
        tau1 = 0.029999733,
        tau2 = 0.006626032
    )
    axis(
        1,
        outer = FALSE,
        lwd = 0,
        lwd.ticks = .2,
        pos = -.3,
        cex.axis = 1.5,
        at = c(0, 40, 80, 120, 160, 200),
        labels = format(c(0, 40, 80, 120, 160, 200))
    )

    x11()
    componentEvents(
        co1 = 1,
        co2 = 1,
        start1 = 20,
        start2 = 20,
        dt = 0.001,
        time = 80,
        tau1 = 0.029999733,
        tau2 = 0.006626032
    )
    axis(
        1,
        outer = FALSE,
        lwd = 0,        lwd.ticks = .2,
        pos = -.3,
        cex.axis = 1.5,
        at = c(0, 40, 80, 120, 160, 200),
        labels = format(c(0, 40, 80, 120, 160, 200))
    )
}


#' Bass Model
#'
#' Fits the Bass Diffusion model. In particular, fits an observed curve of
#' proportions of adopters to \eqn{F(t)}, the proportion of adopters at time
#' \eqn{t}, finding the corresponding coeficients \eqn{p}, Innovation rate,
#' and \eqn{q}, imitation rate.
#'
#' @param Time Integer vector with values greater than 0. The \eqn{t} parameter.
#' @param p Numeric scalar. Coefficient of innovation.
#' @param q Numeric scalar. Coefficient of imitation.
#' @param dat Either a diffnet object, or a numeric vector.
#' Observed cumulative proportion of adopters.
#' @param x An object of class \code{diffnet_bass}.
#' @param ... Further arguments passed to the method.
#'
#' @details The function fits the bass model with parameters \eqn{[p, q]} for
#' values \eqn{t = 1, 2, \dots, T}, in particular, it fits the following function:
#'
#' \deqn{
#'   F(t) = \frac{1 - \exp{-(p+q)t}}{1 + \frac{q}{p}\exp{-(p+q)t}}
#' }{
#'   F(t) = [1 - exp(-(p + q)*t)]/[1 + exp(-(p + q)*t)*(q/p)]
#' }
#'
#' Which is implemented in the \code{bass_F} function. The proportion of adopters
#' at time \eqn{t}, \eqn{f(t)} is:
#'
#' \deqn{
#'   f(t) = \left\{\begin{array}{ll}
#'   F(t), & t = 1 \\
#'   F(t) - F(t-1), & t > 1
#'   \end{array}\right.
#' }{
#'   f(t) = ifelse(t == 1, F(t), F(t) - F(t-1))
#' }
#'
#' and it's implemented in the \code{bass_f} function.
#'
#' For testing purposes only, the gradient of \eqn{F} with respect to \eqn{p}
#' and \eqn{q} is implemented in \code{bass_dF}.
#'
#' The estimation is done using \code{\link[stats:nls]{nls}}.
#'
#'
#' @return An object of class \code{nls} and \code{diffnet_bass}. For more
#' details, see \code{\link[stats:nls]{nls}} in the \pkg{stats} package.
#'
#' @examples
#' # Fitting the model for the Brazilian Farmers Data --------------------------
#' data(brfarmersDiffNet)
#' ans <- fitbass(brfarmersDiffNet)
#'
#' # All the methods that work for the -nls- object work here
#' ans
#' summary(ans)
#' coef(ans)
#' vcov(ans)
#'
#' # And the plot method returns both, fitted and observed curve
#' plot(ans)
#'
#' @references
#' Bass's Basement Institute Institute. The Bass Model. (2010).
#' Available at: \url{http://www.bassbasement.org/BassModel/Default.aspx}. (Accessed: 29th March 2017)
#' @name bass
#' @author George G. Vega Yon
#' @family statistics
NULL

#' @rdname bass
#' @export
fitbass <- function(dat, ...) UseMethod("fitbass")

#' @export
#' @rdname bass
fitbass.diffnet <- function(dat, ...) {
  .fitbass(cumulative_adopt_count(dat$cumadopt)["prop",], ...)
}

#' @export
#' @rdname bass
fitbass.default <- function(dat, ...) {
  .fitbass(as.vector(dat), ...)
}

.fitbass <- function(dat, ...) {

  # Constants
  Time <- seq_along(dat)

  # # Optimization (Fit nonlinear regression)
  ans <- stats::nls(dat ~ bass_F(Time, p, q), start=list(p = dat[1], q = .5 ),...)

  structure(c(
    ans,
    list(
      q      = stats::coef(ans)[2],
      p      = stats::coef(ans)[1],
      Fvals  = bass_F(Time, stats::coef(ans)[1], stats::coef(ans)[2]),
      fvals  = bass_f(Time, stats::coef(ans)[1], stats::coef(ans)[2]),
      nper   = length(Time),
      dat    = dat,
      Time   = Time
    )
  ), class = c("diffnet_bass", class(ans)))

}

#' @rdname bass
#' @param y Integer vector. Time (label).
#' @param pch Passed to \code{\link[graphics:plot]{matplot}}.
#' @param main Passed to \code{\link[graphics:plot]{matplot}}.
#' @param xlab Character scalar. Label of the \code{x} axis.
#' @param ylab Character scalar. Label of the \code{y} axis.
#' @param type Passed to \code{\link[graphics:plot]{matplot}}.
#' @param lty Passed to \code{\link[graphics:plot]{matplot}}.
#' @param col Passed to \code{\link[graphics:plot]{matplot}}.
#' @param bg Passed to \code{\link[graphics:plot]{matplot}}.
#' @param include.legend Logical scalar. When \code{TRUE}, draws a legend.
#' @param add Passed to \code{\link[graphics:plot]{matplot}}.
#' @export
plot.diffnet_bass <- function(
  x, y=1:length(x$m$lhs()), add=FALSE,
  pch  = c(21,24),
  main = "Bass Diffusion Model",
  ylab = "Proportion of adopters",
  xlab = "Time",
  type = c("b", "b"),
  lty  = c(2,1),
  col  = c("black","black"),
  bg   = c("lightblue","gray"),
  include.legend = TRUE,
  ...) {

  if (length(type) == 1) type <- rep(type,2)
  if (length(pch) == 1)  pch  <- rep(pch,2)
  if (length(lty) == 1)  lty  <- rep(lty,2)
  if (length(col) == 1)  lty  <- rep(col,2)
  if (length(bg) == 1)   bg   <- rep(bg,2)

  mat <- with(x, cbind(m$lhs(), m$fitted()))

  matplot(mat, xlab = xlab, ylab = ylab, main=main,
          ylim = c(0,1), type=type, lty=lty, bg=bg, col=col,
          pch=pch, add=add, ...)

  # Adding legend
  if (!add && include.legend)
    legend(
      "topleft", bty="n",
      legend = c("Observed Cumulative Adopters", "Predicted Cumulative Adopters"),
      col=col, pt.bg=bg, pch=pch
      )

  invisible(x)

}

#' @rdname bass
#' @export
bass_F <- function(Time, p, q) {
  (1 - exp(-(p + q)*Time))/(1 + (q/p)*exp(-(p+q)*Time))
}

#' @rdname bass
#' @export
bass_dF <- function(p, q, Time) {
  expv <- exp((p+q)*Time)

  rbind(
    expv * p^2*Time + q*(expv*(1 + p*Time) - 1)/
      (expv*p + q)^2,
    p * (1 + expv*(p*Time + q*Time - 1)) /
      (expv*p + q)^2
  )
}

#' @rdname bass
#' @export
bass_f <- function(Time, p, q) {
  ifelse(Time==1, bass_F(Time, p, q), bass_F(Time, p, q) - bass_F(Time-1, p, q))
}


#' @title calcKristThrustFactor
#'
#' @description Calculate the thrust deduction factor (\code{thrustFactor})
#'(dimensionless) using the Kristensen method.
#'
#' @param shipType Ship type (vector of strings, see \code{\link{calcShipType}}),
#' determined by Stat 5 code, \itemize{
#' \item"container.ship"
#' \item"bulk.carrier"
#' \item"tanker"
#' \item"general.cargo"
#' \item"vehicle.carrier"
#' \item"reefer"
#' \item"ro.ro"
#' \item"passenger"
#' \item"tug"
#' \item"misc"
#'}
#'@param breadth Moulded breadth (vector of numericals, m)
#'@param lwl Waterline Length (vector of numericals, m) (see
#'\code{\link{calclwl}})
#'@param Cbw Waterline block coefficient (vector of numericals, dimensionless)
#' (see \code{\link{calcCbw}})
#'@param propDiam Propeller diameter (vector of numericals, m) (see
#' \code{\link{calcPropDia}})
#'@param M Fineness/slenderness coefficient (vector of numericals,
#' dimensionless) (see \code{\link{calcShipM}})
#'@param nProp Number of propellers (vector of numericals, see
#' \code{\link{calcPropNum}})
#' @param tankerBulkCarrierShipTypes Ship types specified in input
#'\code{shipTypes} to be modeled as tankers and bulk carriers vessels
#' (vector of strings)
#'
#' @details
#'Thrust deduction factor is a component of hull efficiency as well as a
#'component of propeller efficiency. It describes the increase in resistance on
#'the hull from water getting sucked back towards the propeller.
#'
#' This method this requires ship types to be grouped. Use the
#' \code{tankerBulkCarrierShipTypes} grouping  parameters to provide these ship
#' type groupings. Any ship types not included in this grouping will be considered
#' as miscellaneous vessels.
#'
#' @return \code{thrustFactor} (vector of numericals, dimensionless)
#'
#' @references
#'Kristensen, H. O. and Lutzen, M. 2013. "Prediction of Resistance and Propulsion
#'Power of Ships."
#'
#'\href{https://gitlab.gbar.dtu.dk/oceanwave3d/Ship-Desmo}{Kristensen, H. O.
#'"Ship-Desmo-Tool." https://gitlab.gbar.dtu.dk/oceanwave3d/Ship-Desmo}
#'
#'@seealso \itemize{
#'\item \code{\link{calclwl}}
#'\item \code{\link{calcCbw}}
#'\item \code{\link{calcPropDia}}
#'\item \code{\link{calcShipM}}
#'\item \code{\link{calcPropNum}}
#'\item \code{\link{calcShipType}}
#'}
#'
#' @family Kristensen Calculations
#'
#' @examples
#' calcKristThrustFactor(c("bulk.carrier","container.ship"),
#'                       c(32.25,49),
#'                       c(218.75,400),
#'                       c(0.8,0.75),
#'                       c(6.7,9.6),
#'                       c(5.2,6.7),
#'                       c(1,1))
#'
#' calcKristThrustFactor("bulk.carrier", 32.25, 218.75, 0.8, 6.7,5.2,1)
#'
#' @export

calcKristThrustFactor <- function(shipType, breadth, lwl, Cbw, propDiam,M,nProp,
                                  tankerBulkCarrierShipTypes=c("tanker","chemical.tanker","liquified.gas.tanker","oil.tanker","other.tanker","bulk.carrier")
){

  thrustFactor<-ifelse(nProp==1,
                       ifelse(shipType%in%tankerBulkCarrierShipTypes,
                              (#single prop case
                                #t1
                                #d
                          (((0.625*breadth)/lwl)+0.08)+
                            #e
                            (0.165-((0.25*breadth)/lwl))/
                            (#f
                              (825-((8060*breadth)/lwl)+
                                 20300*((breadth/lwl)^2))*
                                (0.98-Cbw)^3+1)+
                            #t2=0 since we assume F_a=0
                            #t3
                            (2*((propDiam/lwl)-0.04))
                        )#thrust correction
                        -0.26+0.04*M,
                        (#single prop case for non bulk carriers non tankers
                          #t1
                          #d
                          (((0.625*breadth)/lwl)+0.08)+
                            #e
                            (0.165-((0.25*breadth)/lwl))/
                            (#f
                              (825-((8060*breadth)/lwl)+
                                 20300*((breadth/lwl)^2))*
                                (0.98-Cbw)^3+1)+
                            #t2=0 since we assume F_a=0
                            #t3
                            (2*((propDiam/lwl)-0.04))
                        )),
                         (#twin prop case
                          0.0665+0.62833*(1.133*(Cbw^2)-0.797*Cbw+0.215)
                        )#end of twin prop case

            )#end elseif

   return(thrustFactor)

}


HIT <- 1
STICK <- 2
BJCard <- function()
  return(sample(10,1))

StateInput <- function () {
  return ( c(sample(10, 1), sample(10, 1), 0))
}
StepFunc <- function (s, a) {
  if(s[3]==1)
    return(list(s, 0))

  NewState <- s
  BJReward <- 0

  if(a==1) {
    NewState[2] <- s[2] + BJCard()
    if (NewState[2]>21 || NewState[2]<1) {
      NewState[3] <- 1
      BJReward <- -1
    }
  }
  else {
    NewState[3] <- 1
    DealerWork <- FALSE
    DealerSum <- s[1]
    while(!DealerWork) {
      DealerSum <- DealerSum + BJCard()
      if (DealerSum>21) {
        DealerWork <- TRUE
        BJReward <- 1
      } else if (DealerSum >= 17) {
        DealerWork <- TRUE
        if(DealerSum==s[2])
          BJReward <- 0
        else
          BJReward <- 2*as.integer(DealerSum<s[2])-1
      }
    }
  }
  return(list(NewState, BJReward))
}

ActionsEpsValGreedy <- function(s, QFunc, EpsVal) {
  if(runif(1)<EpsVal)
    return(sample(1:2, 1))
  else
    return(which.max(QFunc[s[1],s[2],]))
}

library("foreach")

MontecarloFunc <- function(NumEpisode){
  QFunc <- array(0, dim=c(10, 21, 2))
  N <- array(0, c(10,21,2))
  N0=100

  policy <- function(s) {
    ActionsEpsValGreedy(s, QFunc, N0/(sum(N[s[1], s[2],])+N0))
  }

  foreach(i=1:NumEpisode) %do% {
    s <- StateInput()
    SumReturns <- 0
    N.episode <- array(0, c(10,21,2))

    while(s[3]==0) {
      a <- policy(s)
      N.episode[s[1], s[2], a] <- N.episode[s[1], s[2], a] + 1
      StateReward <- StepFunc(s, a)
      s <- StateReward[[1]]
      SumReturns <- SumReturns + StateReward[[2]]
    }
    IndexValue <- which(N.episode!=0)
    N[IndexValue] <- (N[IndexValue]+N.episode[IndexValue])
    QFunc[IndexValue] <- QFunc[IndexValue] + (SumReturns- QFunc[IndexValue]) / N[IndexValue]
  }
  return(list(QFunc=QFunc, N=N))
}

MCModel <- MontecarloFunc(NumEpisode=100000)

StateValueFunc <- apply(MCModel$QFunc, MARGIN=c(1,2), FUN=max)
persp(StateValueFunc, x=1:10, y=1:21, theta=50, phi=35, d=1.9, expand=0.3, border=NULL, ticktype="detailed",
      shade=0.6, xlab="Dealer exposed card", ylab="Player sum", zlab="Value", nticks=10)


getwd() # day3 folder

fileName <- 'day3\\input.txt'
fileCon <- file(fileName,'rb')
input <- readChar(fileCon, file.info(fileName)$size)
close(fileCon)
# print(input)

nVisited <- function(input){
    xyMin = 100 # increased until the code dont give errors anymore
    map <- matrix(0, nrow = 2*xyMin, ncol = 2*xyMin)
    currX = 0
    currY = 0
    nInstructions = nchar(input)
    for (i in 1:nInstructions){
        instr = substr(input,i,i)
        if (instr == "<"){
            currX <- currX -1
        } else if (instr=="^"){
            currY <- currY+1
        }else if (instr=="v"){
            currY <- currY-1
        }else if (instr==">"){
            currX <- currX+1
        }
        map[currX+xyMin,currY+xyMin] <- map[currX+xyMin,currY+xyMin] + 1
    }
    return(sum(map>0))
}

stopifnot(nVisited(input)==2572) # solution A

solve.B <- function(input){
    stopifnot(nchar(input)%%2==0) # make sure sanda and RoboSanta get the same amount of presents

    xyMin = 100 # increased until the code dont give errors anymore
    map <- matrix(0, nrow = 2*xyMin, ncol = 2*xyMin)
    currX = 0
    currY = 0
    roboX = 0
    roboY = 0
    map[currX+xyMin,currY+xyMin] <- map[currX+xyMin,currY+xyMin] + 1

    nInstructions = nchar(input)
    for (i in 1:(nInstructions/2)){
        zerosIdxI = i-1
        instrIdx = 2*zerosIdxI +1
        roboInstrIdx = instrIdx +1
        # print(c(i,zerosIdxI,instrIdx,roboInstrIdx))
        instr = substr(input,instrIdx,instrIdx)
        roboInstr = substr(input,roboInstrIdx,roboInstrIdx)
        # print(c(instr,roboInstr))
        if (instr == "<"){
            currX <- currX -1
        } else if (instr=="^"){
            currY <- currY+1
        }else if (instr=="v"){
            currY <- currY-1
        }else if (instr==">"){
            currX <- currX+1
        }
        if (roboInstr == "<"){
            roboX <- roboX -1
        } else if (roboInstr=="^"){
            roboY <- roboY+1
        }else if (roboInstr=="v"){
            roboY <- roboY-1
        }else if (roboInstr==">"){
            roboX <- roboX+1
        }
        map[currX+xyMin,currY+xyMin] <- map[currX+xyMin,currY+xyMin] + 1
        map[roboX+xyMin,roboY+xyMin] <- map[roboX+xyMin,roboY+xyMin] + 1
    }
    ans <- sum(map>0)
    # print(c(currX,currY,roboX,roboY,ans))
    return(ans)
}

stopifnot(solve.B("^v")==3,solve.B("^>v<")==3,solve.B("^v^v^v^v^v")==11,
solve.B(input)==2631 # answer B
)


\name{Jeong}
\alias{Jeong}
%- Also NEED an '\alias' for EACH other topic documented here.
\title{
 Jeong's method for estimating  the preferential attachment function
}
\description{
  This function estimates the preferential attachment function by Jeong's method.
}
\usage{
Jeong(net_object                               ,
      net_stat  = get_statistics(net_object)   ,
      T_0_start = 0                            ,
      T_0_end   = round(net_stat$T * 0.75)     ,
      T_1_start = T_0_end + 1                  ,
      T_1_end   = net_stat$T                   ,
      interpolate = FALSE)
}
%- maybe also 'usage' for other objects documented here.
\arguments{
 \item{net_object}{
    an object of class \code{PAFit_net} that contains the network.
  }
  \item{net_stat}{
    An object of class \code{PAFit_data} which contains summerized statistics needed in estimation. This object is created by the function \code{\link{get_statistics}}. Default value is \code{ get_statistics(net_object)}.
  }
  \item{T_0_start}{Positive integer. The starting time-step of the \code{T_0_interval}. Default value is \code{0}.}
  \item{T_0_end}{Positive integer. The ending time-step of \code{T_0_interval}. Default value is \code{round(net_stat$T * 0.75)}.}

  \item{T_1_start}{Positive integer. The starting time-step of the \code{T_1_interval}. Default value is \code{T_0_end + 1}.}
  \item{T_1_end}{Positive integer. The ending time-step of \code{T_1_interval}. Default value is \code{net_stat$T}.}

  \item{interpolate}{
    Logical. If \code{TRUE} then all the gaps in the estimated PA function are interpolated by linear interpolating in logarithm scale. Default value is \code{FALSE}.
  }
}
\value{
  Outputs an \code{PA_result} object which contains the estimated attachment function. In particular, it contains the following field:
     \itemize{
    \item \code{k} and \code{A}: a degree vector and the estimated PA function.

    \item \code{center_k} and \code{theta}: when we perform binning, these are the centers of the bins and the estimated PA values for those bins.
    \item \code{g}: the number of bins used.
    \item \code{alpha} and \code{ci}: \code{alpha} is the estimated attachment exponenet \eqn{\alpha} (when assume \eqn{A_k = k^\alpha}), while \code{ci} is the confidence interval.
    \item \code{loglinear_fit}: this is the fitting result when we estimate \eqn{\alpha}.
}
}
\author{
  Thong Pham \email{thongpham@thongpham.net}
}
\references{
  1. Jeong, H., \enc{Néda}{Neda}, Z. & \enc{Barabási}{Barabasi}, A. . Measuring preferential attachment in evolving networks. Europhysics Letters. 2003;61(61):567–572. doi: 10.1209/epl/i2003-00166-9 (\url{https://iopscience.iop.org/article/10.1209/epl/i2003-00166-9}).
}
\seealso{

 See \code{\link{get_statistics}} for how to create summerized statistics needed in this function.

See \code{\link{Newman}} and \code{\link{only_A_estimate}} for other methods to estimate the attachment function in isolation.

}

\examples{
  library("PAFit")
  net        <- generate_net(N = 1000 , m = 1 , mode = 1 , alpha = 1 , s = 0)
  net_stats  <- get_statistics(net)
  result     <- Jeong(net, net_stats)
  # true function
  true_A     <- result$center_k
  #plot the estimated attachment function
  plot(result , net_stats)
  lines(result$center_k, true_A, col = "red") # true line
  legend("topleft" , legend = "True function" , col = "red" , lty = 1 , bty = "n")
}
\concept{preferential attachment}
\concept{attachment function}


#' @param X matrix. Rows are genes. Columns are samples. Row names are symbols.
#' @param gene_sets list. Each element is a string vector with gene symbols.
#' @param alpha numeric. Parameter for ssGSEA, the default is 0.25
#' @param scale logical. If True, normalize the scores by number of genes in the gene sets.
#' @param norm logical. If True, normalize the scores by the absolute difference between max and min values.
#' @param single logical. If True, use ssGSEA algorithm, otherwise use GSEA.
#'
#' @return matrix containing enrichment scroes. Rows are gene sets, columns are samples.
#'
#' @examples
#' # Create a fake matrix
# m = 100
# n = 100
# set.seed(1)
# X = matrix(rnorm(m*n), m, n)
# # # Assign 'gene symbols' to row names
# rownames(X) = 1:m
# # Create 3 gene sets
# gene_sets = list(a = sample(m, 5), b = sample(m, 5), c = sample(m, 5))
# system.time(assign('a', GSVA::gsva(X, gene_sets, method = 'ssgsea')))
# system.time(assign('b', ssgsea(X, gene_sets, scale = F, norm = T)))
# identical(a, b)
set.seed(1)
ssgsea = function(X, gene_sets, alpha = 0.25, scale = T, norm = F, single = T) {
  row_names = rownames(X)
  num_genes = nrow(X)
  gene_sets = lapply(gene_sets, function(genes) {which(row_names %in% genes)})

  # Ranks for genes
  R = matrixStats::colRanks(X, preserveShape = T, ties.method = 'average')

  # Calculate enrichment score (es) for each sample (column)
  es = apply(R, 2, function(R_col) {
    gene_ranks = order(R_col, decreasing = TRUE)

    # Calc es for each gene set
    es_sample = sapply(gene_sets, function(gene_set_idx) {
      # pos: match (within the gene set)
      # neg: non-match (outside the gene set)
      indicator_pos = gene_ranks %in% gene_set_idx
      indicator_neg = !indicator_pos

      rank_alpha  = (R_col[gene_ranks] * indicator_pos) ^ alpha

      step_cdf_pos = cumsum(rank_alpha)    / sum(rank_alpha)
      step_cdf_neg = cumsum(indicator_neg) / sum(indicator_neg)

      step_cdf_diff = step_cdf_pos - step_cdf_neg

      # Normalize by gene number
      if (scale) step_cdf_diff = step_cdf_diff / num_genes

      # Use ssGSEA or not
      if (single) {
        sum(step_cdf_diff)
      } else {
        step_cdf_diff[which.max(abs(step_cdf_diff))]
      }
    })
    unlist(es_sample)
  })

  if (length(gene_sets) == 1) es = matrix(es, nrow = 1)

  # Normalize by absolute diff between max and min
  if (norm) es = es / diff(range(es))

  # Prepare output
  rownames(es) = names(gene_sets)
  colnames(es) = colnames(X)
  return(es)
}

# setwd("~/github/GSEApy/tests/data")
# comparison with gseapy.ssgsea
X2 = read.table("./data/testSet_rand1200.gct", row.names = 1, header = T,
                comment='#', sep="\t", stringsAsFactors = F)
X3 = as.matrix.data.frame(X2[,-1])
gene_sets = fgsea::gmtPathways("./data/randomSets.gmt")

# test
system.time(assign('a', GSVA::gsva(X3, gene_sets, method = 'ssgsea')))
system.time(assign('b', ssgsea(X3, gene_sets, scale = F, norm = T)))
system.time(assign('b2', ssgsea(X3, gene_sets, scale = T, norm = F)))

identical(a, b)
print("TEST with GSVA::gsva(method='ssgsea')")
print(a)

print("\n\n\n")
print("TEST with ssgsea, NES")
print(b)

print("\n\n\n")
print("TEST with ssgsea, Scaled ES")
print(b2)


#' Is a set of chains sufficiently converged?
#'
#' Determines if a set of MCMC chains is sufficiently converged based on the proportion of coefficients for which rhat is < a threshold value.
#' @param mcmc An object of class \code{\link[coda]{mcmc.list}}.
#' @param rhatThresh Numeric, value of rhat below which a chain is considered to be converged (default = 1.1).
#' @param minConv Numeric in the range [0, 1], proportion of rhat values to consider a set of chains "sufficiently" converged. Typically this is 0 (default).
#' @param ignore Character or character vector, names of nodes to ignore when calculating rhat. These can include regex expressions (e.g., \code{x[[]}). Default is \code{NULL} (calculate rhat for all nodes).
#' @return List with three elements:
#' \itemize{
#' 		\item Logical (if proportion of rhats is below the threshold).
#' 		\item Proportion of nodes that did not converge.
#'		\item Vector of node names that did not converge.
#'		\item Vector of node names with \code{NA} for rhat value.
#' @export

rhatStats <- function(mcmc, rhatThresh = 1.1, minConv = 0, ignore = NULL) {

	nchains <- length(mcmc)

	if (!is.null(ignore)) {
		for (chain in 1:nchains) {
			for (ig in ignore) {
				mcmc[[chain]] <- mcmc[[chain]][ , !grepl(colnames(mcmc[[chain]]), pattern=ig)]
			}
		}
	}

	rhat <- wiqid::simpleRhat(mcmc, nchains)

	propUnconv <- sum(rhat >= rhatThresh, na.rm=TRUE) / sum(!is.na(rhat))
	sufficient <- (propUnconv < minConv)
	unconv <- names(rhat[rhat > rhatThresh])
	nas <- names(rhat)[is.na(rhat)]

	returns <- list(sufficient=sufficient, propUnconv=propUnconv, unconv=unconv, nas=nas, rhat=rhat)
	returns

}


require(Matrix)

##
# sample reported deaths after rep.day (if Inf) predicit day
# assuming Multionimal distribution
#
#  samples  - (int) how many samples should be done for each N (cheap)
#  deaths   - (N x 1) true number of deaths each day
#  P        - (N x N) matrix of probabilites (only upper triangular part relevant)
#  Reported - (N x N) matrix of reported deaths cumlative (only upper triangular relevant)
#  alpha    - (N x 1) stepsizes
#  true.dat  - (int) how many days after recording should one sample
##
sample.deaths <- function(samples, deaths, P, Reported, alpha, true.day = 0){

  N <- length(deaths)
  acc <- rep(0,N)
  for(i in 1:N){
    if(i > true.day){
      P_i         = P[i,i:N]
      Reported_i  = Reported[i,i:N]
      index = is.na(Reported_i)==F
      P_i         = P_i[index]
      Reported_i  = Reported_i[index]
      lik_i <- loglikDeathsGivenProb(deaths[i], P_i, Reported_i)
      for(j in 1:samples){
        death_star <- sample((deaths[i]-alpha[i]):(deaths[i]+alpha[i]), 1)
        lik_start <- loglikDeathsGivenProb(death_star,  P_i, Reported_i)
        if(log(runif(1)) < lik_start-lik_i){
          lik_i = lik_start
          deaths[i] <- death_star
          acc[i] = acc[i] + 1
        }
      }
    }
  }
  return(list(deaths=deaths, acc = acc))
}

##
# sample reported deaths after rep.day (if Inf) predicit day using Beta binomial dist
#
#  samples  - (int) how many samples should be done for each N (cheap)
#  deaths   - (N x 1) true number of deaths each day
#  alpha     - (N x N) matrix of beta binom parameter (only upper triangular part relevant)
#  beta      - (N x N) matrix of beta binom parameter (only upper triangular part relevant)
#  Reported - (N x N) matrix of reported deaths cumlative (only upper triangular relevant)
#  alpha.MCMC    - (N x 1) stepsizes
#  true.dat  - (int) how many days after recording should one sample
#  prior     - (function) should  return log of prior density (use N and i)
##
sample.deathsBB <- function(samples, deaths, alpha, beta, Reported, alpha.MCMC, true.day = 0 , prior=NULL){

  N <- length(deaths)
  acc <- rep(0,N)
  for(i in 1:N){
    if(i > true.day){
      alpha_i     = alpha[i,i:N]
      beta_i      = beta[i,i:N]
      Reported_i  = Reported[i,i:N]
      index = is.na(Reported_i)==F
      alpha_i  = alpha_i[index]
      beta_i  = beta_i[index]
      Reported_i  = Reported_i[index]

      lik_i <- loglikDeathsGivenProbBB(deaths[i],alpha_i , beta_i, Reported_i)
      if(is.null(prior)==F)
        lik_i <- lik_i + prior(deaths[i], i)
      for(j in 1:samples){
        death_star <- sample((deaths[i]-alpha.MCMC[i]):(deaths[i]+alpha.MCMC[i]), 1)

        lik_star <- loglikDeathsGivenProbBB(death_star, alpha_i , beta_i, Reported_i)
        if(is.null(prior)==F)
          lik_star <- lik_star + prior(death_star, i)

        if(log(runif(1)) < lik_star-lik_i){
          lik_i = lik_star
          deaths[i] <- death_star
          acc[i] = acc[i] + 1
        }
      }
    }
  }
  return(list(deaths=deaths, acc = acc))
}

##
# fill report using binimoal beta
#
#
##
fill.ReportBB <- function(deaths, Alpha, Beta, Reported, maxusage.day){
  N <- length(deaths)
  N_2 <- dim(Alpha)[2]
  for(i in 1:N){
      Alpha_i         = Alpha[i,i:N_2]
      Beta_i          = Beta[i,i:N_2]
      Reported_i  = Reported[i,i:N_2]
      index = is.na(Reported_i)==T
      for(j in min(which(index)):length(Reported_i)){
        p <- rbeta(1, Alpha_i[j], Beta_i[j])

        if(i> maxusage.day){
          Reported_i[j] <- rbinom(1, size=deaths[i] - Reported_i[j-1], prob = p) + Reported_i[j-1]
        }else{
        Reported_i[j] <- Reported_i[j-1]
        }
      }
    Reported[i,i:N_2] = Reported_i
  }
  return(Reported)
}

##
# fill report
#
#
##
fill.Report <- function(deaths, P, Reported){
  N <- length(deaths)
  N_2 <- dim(P)[2]
  for(i in 1:N){
      P_i         = P[i,i:N_2]
      Reported_i  = Reported[i,i:N_2]
      index = is.na(Reported_i)==T
      for(j in min(which(index)):length(Reported_i)){
        Reported_i[j] <- rbinom(1, size=deaths[i] - Reported_i[j-1], prob =  P_i[j]) + Reported_i[j-1]
      }
      Reported[i,i:N_2] = Reported_i
  }
  return(Reported)
}
###
#
# posterior sampling of deaths given Prob vec
#
# P           - (N x N) probability matrix over probability of detecting reminder
# Reported    - (N x N) matrix of reported deaths cumlative (only upper triangular relevant)
# Predict.day - (int) which day to of reporting to predict
# sim         - (2 x 1) MCMC samples inner loop and outer loop
# alpha       - (N x 1) stepsizes
#
###
death.givenProb <- function(P, Reported ,Predict.day = Inf,sim=c(2000,10), alpha = NULL){

  N <- dim(P)[1]
  if(is.null(alpha))
    alpha <- rep(4, N)

  deaths <- matrix(NA, nrow=sim[1], ncol = N)
  deaths_est <- apply(Reported, 1, max, na.rm=T)

  burnin = ceiling(0.3*sim[1])
  for(i in 1:(sim[1] + burnin -1)){

    res <- sample.deaths(sim[2],deaths_est, P, Reported, alpha,rep.day=Predict.day)
    deaths_est <- res$deaths
    if(i < burnin){
      alpha[res$acc/sim[2] > 0.3] <- alpha[res$acc/sim[2] > 0.3] +1
      alpha[res$acc/sim[2] < 0.3] <- alpha[res$acc/sim[2] < 0.3] -1
      alpha[alpha<1] <- 1
    }else{
      deaths[i - burnin +1,] = deaths_est
    }
  }
  return(deaths)
}

##
# log liklihood of obseving report given death and prob
#  deaths  - (int) true number of deaths
#  p       - (n x 1) probability of report
#  report  - (n x 1) reported deaths culmative each date
##
loglikDeathsGivenProb <- function(death, p, report){

  if(death < max(report,na.rm=T))
    return(-Inf)
  n <- length(p)
  if(is.na(report[1])){
    #we dont have data from day one

    ndeaths <- diff(report[is.na(report)==F])
    report_adj <- report[1:(n-1)]
    report_adj <- report_adj[is.na(report_adj)==F]
  }else{
    ndeaths <- c(report[1],diff(report))
    if(n>1){
      report_adj <- c(0, report[1:(n-1)])
    }else{
      report_adj <- 0
    }
  }

  ndeaths[ndeaths <0 ] = 0

  return(sum(dbinom(ndeaths,  death - report_adj,  prob = p[is.na(p)==F], log=T )))
}

##
# build holiday covariates vector for holiday
#
##
#  holidays - (N x 1) true if day is holiday false else
##
buildXholiday <- function(N,holidays){
  ##
  # base matrix
  ##
  index_base <- t(matrix(rep(holidays,N),ncol = N))

  index_base <- index_base[upper.tri(index_base,diag=T)]
  index_base <- 1* index_base
  #sparse matrix
  i_base <- 1:length(index_base)
  i_base <- i_base[index_base==1]
  return(sparseMatrix(j=rep(1,length(i_base)),i=i_base,  dims=c(length(index_base), 1)))
}

buildXall <- function(N){
  ##
  # base matrix
  ##
  index_base <- t(matrix(1:N^2,ncol = N, nrow=N))

  index_base <- index_base[upper.tri(index_base,diag=T)]
  #sparse matrix
  i_base <- 1:length(index_base)
  j_base <- 1:length(index_base)
  return(sparseMatrix(j=j_base,i=j_base))
}

##
# build day effect matrix
#  nDayEffects - number of speical days effect (1- first day, 2- first + second day)
#  N - number of days
#  nDayEffects - how many day covariate effect to create
##
buildXdayeffect <- function(N, nDayEffects = 1){
  nDayEffects <- min(N,nDayEffects)
  index_days <- toeplitz(c( (1:nDayEffects), rep(0,N -nDayEffects)))
  index_days <- index_days[upper.tri(index_days,diag=T)]
  j_ <-  index_days[index_days>0]
  i_base <- 1:length(index_days)
  i_ <-  i_base[index_days>0]
  return(sparseMatrix(i=i_,j=j_ , dims=c(length(index_days), nDayEffects)  ) )
}
##
# build day mixed effect matrix
#  nDayEffects - number of speical days effect (1- first day, 2- first + second day)
#  N - number of days
#  nDayEffects - how many day covariate effect to create
##
buildXmixeddayeffect <- function(N, nDayEffects = 1){

  index = rep(0,N)
  index[nDayEffects] = 1
  index_days <- toeplitz(index)
  index_days <- index_days[upper.tri(index_days,diag=T)]
  j_ <-  1:sum(index_days[index_days>0])
  i_base <- 1:length(index_days)
  i_ <-  i_base[index_days>0]
  return(sparseMatrix(i=i_,j=j_ , dims=c(length(index_days), sum(index_days[index_days>0])))  )
}
##
# building an X matrix such that each day has a fixed effect
#
##
buildXday <- function(N){
  ##
  # base matrix
  ##
  index_base <- t(matrix(rep(1:N,N),ncol = N))
  index_base <- index_base[upper.tri(index_base,diag=T)]
  #sparse matrix
  j_base <- 1:length(index_base)
  #adding mean effect
  j_ <- c(index_base, rep(N+1,length(index_base)))
  i_ <- c(j_base, 1:length(index_base))
  #day effects
  if(nDayEffects > 0){
    index_days <- toeplitz(c( N+1 + (1:nDayEffects), rep(0,N -nDayEffects)))
    index_days <- index_days[upper.tri(index_days,diag=T)]
    j_ <- c(j_, index_days[index_days>0])
    i_ <- c(i_, j_base[index_days>0])
  }
  return(sparseMatrix(i=i_,j=j_))
}
##
# transforms data,
# we remove data if negative..
#
#  deaths  - (N x 1) how many has died (thruth)
#  reports - (N x N) reported cumlative deaths
#  maxusage.day - (int) only use data up to maxusage.days i.e.
#                       reports[i,i + maxusage.day - 1]
#
##
newDeaths <-function(deaths, reports,maxusage.day = -1){
  newreport <- reports
  diff.report <- t(diff(t(reports)))
  newreport[upper.tri(newreport)] <- diff.report[upper.tri(diff.report,T)]
  newreport[newreport<0 & is.na(newreport)==F]=0 #fake
  death.rem <- diag(deaths)
  dr<-deaths-reports
  N <- length(deaths)
  dr <- dr[1:(N-1),1:(N-1)]
  death.rem[upper.tri(newreport)] <- dr[upper.tri(dr,T)]
  death.rem[lower.tri(death.rem)] <- NA
  diag(death.rem)[is.na(diag(reports))] <- NA
  if(maxusage.day>0){
    for(i in 1:length(death.rem)){
      if(i + maxusage.day - 1 < N ){
        death.rem[i,(i+maxusage.day):N] = NA
        newreport[i,(i+maxusage.day):N] = NA
      }
    }
  }
  #removing small bugs in reporting
  newreport[death.rem <0 & is.na(death.rem)==F] = 0
  death.rem[death.rem <0& is.na(death.rem)==F] = 0
  index <- (death.rem< newreport) &  is.na(death.rem) ==F
  newreport[index] =death.rem[index]
  return(list(death.remain = death.rem, report.new = newreport))
}

##
# likelihood observations given deaths and probabilites
# model is:
# newreport[upper.tri] \sim   Bin(deaths[upper.tri], logit(X\beta))
#
#' @return obj - list
#'               $loglik  - logliklihood
#'               $grad    - gradient
#'               $hessian - Hessian of the likelihood
##
loglikProb <- function(beta, death.remain, report.new, X){

  N <- dim(report.new)[1]

  index <- upper.tri(death.remain,diag = T)
  y = report.new[index]
  n = death.remain[index]
  index = is.na(y)==F
  X <- X[index,]
  p = as.vector(1/(1+exp(-X%*%beta)))
  y <- y[index]
  n <- n[index]
  lik <- sum(dbinom(y,size = n, prob = as.vector(p), log = T))

  grad <- as.vector(t(X)%*%as.vector(y-n*p))

  Hessian <- -t(X)%*%diag(as.vector(n*p*(1-p)))%*%X
  Hessian <- diag(diag(Hessian))

  return(list(loglik = lik, grad = grad, Hessian = Hessian))
}
loglikPrior <- function(beta, n, sigma, res){
  n <- length(sigma)
  res$grad[1:n] <-  res$grad[1:n]-beta[1:n]/(sigma^2)
  diag(res$Hessian) <- diag(res$Hessian) - c(1/sigma^2, rep(0,length(beta)-n))
  res$loglik <- res$loglik -sum(beta[1:n]^2/(2*sigma^2))
  return(res)
}

loglik <- function(beta,death.remain, report.new, X, sigma){
  res <- loglikProb(beta, death.remain, report.new, X)
  res <- loglikPrior(beta, n, sigma, res)
  return(res)
}

#####
##
# beta binomial part
##
#####

##
# likelihood observations given deaths and probabilites
# model is:
# newreport[upper.tri] \sim   BB(deaths[upper.tri], exp(X\beta_1), exp(X\beta_2))
#
#' @return obj - list
#'               $loglik  - logliklihood
#'               $grad    - gradient
##
loglikProbBB <- function(beta, death.remain, report.new, X, calcH=T){

  p <- dim(X)[2]
  beta_1 <- beta[1:p]
  beta_2 <- beta[(p+1):(2*p)]
  N <- dim(report.new)[1]

  index <- upper.tri(death.remain,diag = T)
  y = report.new[index]
  n = death.remain[index]
  index = is.na(y)==F
  X <- X[index,]
  alpha <- exp(X%*%beta_1)
  beta  <- exp(X%*%beta_2) #unfortunate name
  if(min(1/(alpha+beta)) <0.001)
    return(list(loglik = -Inf, grad = 0, Hessian = 0))
  y <- y[index]
  n <- n[index]

  lik <- sum(dBB(y, size = n, alpha = alpha, beta = beta, log.p=T))
  if(is.na(lik))
    return(list(loglik = -Inf, grad = 0, Hessian = 0))

  grad_lik <- grad_dBB(y, size = n, alpha = alpha, beta = beta)
  grad_alpha <- as.vector(t(X)%*%(alpha*grad_lik$grad_alpha))
  grad_beta  <- as.vector(t(X)%*%(beta*grad_lik$grad_beta))
  if(calcH){
    H_ <- Hessian_dBB(y, size = n, alpha = alpha, beta = beta)
    H_12 <-  t(X)%*%diag(as.vector(alpha*beta*H_$grad_alpha_beta))%*%X
    H_11 <-  t(X)%*%diag(as.vector(alpha^2*H_$grad_alpha_alpha))%*%X
    H_22 <-  t(X)%*%diag(as.vector(beta^2*H_$grad_beta_beta))%*%X
    Hessian <- rbind(cbind(H_11, H_12) ,
                     cbind(H_12, H_22) )
    Hessian  <- diag(diag(Hessian) )
    diag(Hessian) <- diag(Hessian)
  }else{
    Hessian =NULL
  }
  grad =  c(grad_alpha,grad_beta)
    if(sum(is.na(grad))>0)
      return(list(loglik = -Inf, grad = 0, Hessian = 0))
  return(list(loglik = lik, grad = grad, Hessian = Hessian))
}
##
# PRobability of beta binomial
##
dBB<- function(x, size, alpha, beta, log.p = F){
  const = lgamma(size + 1) - lgamma(x + 1) -
    lgamma(size - x + 1)
  ld <- const +
    lgamma(x + alpha) + lgamma(size - x + beta) -
    lgamma(size + alpha + beta) +
    lgamma(alpha + beta) -
    lgamma(alpha) - lgamma(beta)
  if(log.p==F){
    return(exp(ld))
  }
  return(ld)
}
##
# gradient of log of probability beta binomial
# return list,
# [[1]] grad alpha
# [[2]] grad beta
##
grad_dBB<- function(x, size, alpha, beta){
  grad_alpha <- digamma(x + alpha) -
    digamma(size + alpha + beta) +
    digamma(alpha + beta) -
    digamma(alpha)
  grad_beta <- digamma(size - x + beta) -
    digamma(size + alpha + beta) +
    digamma(alpha + beta) -
    digamma(beta)
  return(list(grad_alpha = grad_alpha,
              grad_beta  = grad_beta ))
}
##
# Hessian of log of probability beta binomial
# return list,
# [[1]] grad alpha
# [[2]] grad beta
##
Hessian_dBB<- function(x, size, alpha, beta){
  grad_alpha_alpha <- trigamma(x + alpha) -
    trigamma(size + alpha + beta) +
    trigamma(alpha + beta) -
    trigamma(alpha)
  grad_beta_beta <- trigamma(size - x + beta) -
    trigamma(size + alpha + beta) +
    trigamma(alpha + beta) -
    trigamma(beta)

  grad_alpha_beta <- -trigamma(size + alpha + beta) +
    trigamma(alpha + beta)
  return(list(grad_alpha_alpha = grad_alpha_alpha,
              grad_beta_beta  = grad_beta_beta,
              grad_alpha_beta = grad_alpha_beta))
}

##
# log liklihood of obseving report given death and prob
# density is Beta binomial
#  deaths  - (int) true number of deaths
#  alpha       - (n x 1) bb parameter 1
#  beta        - (n x 1) bb parameter 2
#  report  - (n x 1) reported deaths culmative each date
##
loglikDeathsGivenProbBB <- function(death, alpha, beta, report){

  if(death < max(report,na.rm=T))
    return(-Inf)
  n <- length(alpha)
  if(is.na(report[1])){
    #we dont have data from day one

    ndeaths <- diff(report[is.na(report)==F])
    report_adj <- report[1:(n-1)]
    report_adj <- report_adj[is.na(report_adj)==F]
  }else{
    ndeaths <- c(report[1],diff(report))
    if(n>1){
      report_adj <- c(0, report[1:(n-1)])
    }else{
      report_adj <- 0
    }
  }

  ndeaths[ndeaths <0 ] = 0

  return(sum(dBB(x =ndeaths,
                 size = death - report_adj,
                 alpha = alpha[is.na(alpha)==F],
                 beta = beta[is.na(alpha)==F],
                 log.p=T)))
}

###
#
# posterior sampling of deaths given Prob vec
#
#  alpha     - (N x N) matrix of beta binom parameter (only upper triangular part relevant)
#  beta      - (N x N) matrix of beta binom parameter (only upper triangular part relevant)
# Reported    - (N x N) matrix of reported deaths cumlative (only upper triangular relevant)
# Predict.day - (int) which day to of reporting to predict
# sim         - (2 x 1) MCMC samples inner loop and outer loop
# alpha       - (N x 1) stepsizes
# prior       - (function) prior for N (needs use N and i)
#
###
death.givenParamBB <- function(alpha, beta, Reported,Predict.day = Inf,sim=c(2000,10), alpha.MCMC = NULL, prior=NULL){

  N <- dim(alpha)[1]
  if(is.null(alpha.MCMC))
    alpha.MCMC <- rep(4, N)

  deaths <- matrix(NA, nrow=sim[1], ncol = N)
  deaths_est <- apply(Reported, 1, max, na.rm=T)

  burnin = ceiling(0.3*sim[1])
  for(i in 1:(sim[1] + burnin -1)){
    res <- sample.deathsBB(sim[2],
                           deaths_est,
                           alpha,
                           beta,
                           Reported,
                           alpha.MCMC,
                           rep.day=Predict.day,
                           prior= prior)
    deaths_est <- res$deaths
    if(i < burnin){
      alpha.MCMC[res$acc/sim[2] > 0.3] <- alpha.MCMC[res$acc/sim[2] > 0.3] +1
      alpha.MCMC[res$acc/sim[2] < 0.3] <- alpha.MCMC[res$acc/sim[2] < 0.3] -1
      alpha.MCMC[alpha.MCMC<1] <- 1
    }else{
      deaths[i - burnin +1,] = deaths_est
    }
  }
  return(deaths)
}

##
# buidling the X matrix
#
#
# unique.prob - (int) how many of days shall we have unique probabilites
##
setup_data <- function(N, Predict.day, dates_report, unique.prob=NULL){
  holidays.Sweden <- as.Date(c("2020-04-10","2020-04-13","2020-05-01"))
  X <- buildXdayeffect(N,Predict.day)
  #holidays and weekends

  result$dates_report <- as.Date(dates_report)
  holidays <- weekdays(dates_report)%in%c("Sunday","Saturday") | c(dates_report)%in%c(holidays.Sweden)

  holidays.tommorow <- weekdays(dates_report + 1)%in%c("Sunday","Saturday") |
    (result$dates_report+1)%in%c(holidays.Sweden)
  Xhol <- buildXholiday(N,holidays)
  #Xhol.tommrow <- buildXholiday(N,holidays.tommorow)
  if(is.null(unique.prob)==F)
    X <- cbind(X[,1:unique.prob],rowSums(X[,1:unique.prob])==0)

  X <- cbind(X, Xhol)
  return(X)
}


# preprocessing modis/viirs active fire

# preprocessing ================================================================

read_csv("data/fire_nrt_V1_101893.csv")%>%
  dplyr::mutate(acq_hour = as.numeric(substr(acq_time, start = 1, stop = 2)),
                acq_min = as.numeric(substr(acq_time, start = 3, stop = 4)),
                acq_datetime = ymd_hm(paste0(acq_date, " ", acq_hour, ":", acq_min)),
                acq_year = year(acq_datetime),
                acq_month = month(acq_datetime),
                acq_day = day(acq_datetime),
                solar_offset = longitude / 15,
                hemisphere = ifelse(latitude >= 0, yes = "Northern hemisphere", no = "Southern hemisphere"),
                acq_datetime_local = acq_datetime + as.duration(solar_offset * 60 * 60),
                local_doy = lubridate::yday(acq_datetime_local),
                local_hour_decmin = ((acq_hour) + (acq_min / 60) + solar_offset + 24) %% 24,
                local_solar_hour_decmin_round = round(local_hour_decmin),
                local_solar_hour_decmin_round0.5 = round(local_hour_decmin * 2) / 2,
                h = (local_hour_decmin - 12) * 15 * pi / 180,
                phi = latitude * pi / 180,
                delta = -asin(0.39779 * cos(pi / 180 * (0.98565 * (local_doy + 10) + 360 / pi * 0.0167 * sin(pi / 180 * (0.98565 * (local_doy - 2)))))),
                solar_elev_ang = (asin(sin(phi)*sin(delta) + cos(phi)*cos(delta)*cos(h))) * 180 / pi,
                daynight = ifelse(solar_elev_ang > 0, yes = "day", no = "night")) %>%
  write_csv("data/fire_nrt_australia.csv")

# preprocessing brazil =========================================================

read_csv("data/fire_nrt_V1_104769.csv")%>%
  dplyr::mutate(acq_hour = as.numeric(substr(acq_time, start = 1, stop = 2)),
                acq_min = as.numeric(substr(acq_time, start = 3, stop = 4)),
                acq_datetime = ymd_hm(paste0(acq_date, " ", acq_hour, ":", acq_min)),
                acq_year = year(acq_datetime),
                acq_month = month(acq_datetime),
                acq_day = day(acq_datetime),
                solar_offset = longitude / 15,
                hemisphere = ifelse(latitude >= 0, yes = "Northern hemisphere", no = "Southern hemisphere"),
                acq_datetime_local = acq_datetime + as.duration(solar_offset * 60 * 60),
                local_doy = lubridate::yday(acq_datetime_local),
                local_hour_decmin = ((acq_hour) + (acq_min / 60) + solar_offset + 24) %% 24,
                local_solar_hour_decmin_round = round(local_hour_decmin),
                local_solar_hour_decmin_round0.5 = round(local_hour_decmin * 2) / 2,
                h = (local_hour_decmin - 12) * 15 * pi / 180,
                phi = latitude * pi / 180,
                delta = -asin(0.39779 * cos(pi / 180 * (0.98565 * (local_doy + 10) + 360 / pi * 0.0167 * sin(pi / 180 * (0.98565 * (local_doy - 2)))))),
                solar_elev_ang = (asin(sin(phi)*sin(delta) + cos(phi)*cos(delta)*cos(h))) * 180 / pi,
                daynight = ifelse(solar_elev_ang > 0, yes = "day", no = "night")) %>%
  write_csv("data/fire_nrt_brazil.csv")

# preprocessing tubbs ==============

read_csv("data/fire_archive_V1_104769.csv")%>%
  dplyr::mutate(acq_hour = as.numeric(substr(acq_time, start = 1, stop = 2)),
                acq_min = as.numeric(substr(acq_time, start = 3, stop = 4)),
                acq_datetime = ymd_hm(paste0(acq_date, " ", acq_hour, ":", acq_min)),
                acq_year = year(acq_datetime),
                acq_month = month(acq_datetime),
                acq_day = day(acq_datetime),
                solar_offset = longitude / 15,
                hemisphere = ifelse(latitude >= 0, yes = "Northern hemisphere", no = "Southern hemisphere"),
                acq_datetime_local = acq_datetime + as.duration(solar_offset * 60 * 60),
                local_doy = lubridate::yday(acq_datetime_local),
                local_hour_decmin = ((acq_hour) + (acq_min / 60) + solar_offset + 24) %% 24,
                local_solar_hour_decmin_round = round(local_hour_decmin),
                local_solar_hour_decmin_round0.5 = round(local_hour_decmin * 2) / 2,
                h = (local_hour_decmin - 12) * 15 * pi / 180,
                phi = latitude * pi / 180,
                delta = -asin(0.39779 * cos(pi / 180 * (0.98565 * (local_doy + 10) + 360 / pi * 0.0167 * sin(pi / 180 * (0.98565 * (local_doy - 2)))))),
                solar_elev_ang = (asin(sin(phi)*sin(delta) + cos(phi)*cos(delta)*cos(h))) * 180 / pi,
                daynight = ifelse(solar_elev_ang > 0, yes = "day", no = "night")) %>%
  write_csv("data/fire_archive_brazil.csv")


#' pair_dist
#'
#' vectorized pair-wise distance calculation
#'
#' @param A numeric matrix
#' @param B numeric matrix
#'
.pair_dist <- function(A, B) {

  an <- rowSums(A^2)
  bn <- rowSums(B^2)

  m <- nrow(A)
  n <- nrow(B)

  tmp <- matrix(rep(an, n), nrow=m)
  tmp <- tmp + matrix(rep(bn, m), nrow=m, byrow=TRUE)

  sqrt( tmp - 2 * tcrossprod(A,B) )

}


gevFit <- function(data, aggregation = "annual", nYears, nReplicates = 1, nCovariates = 0, covariates = NULL, dataScaling = 1, locationModel = NULL, scaleModel = NULL, shapeModel = NULL, missingFlag = NULL, returnParams = FALSE, rvInterval = 20, newData = NULL, rvDifference = NULL, maxes = TRUE, optimMethod = "Nelder-Mead"){
  # data should be a 1-d array containing the maxes (or mins) by year (year x (optionally) month x (optionally) location), with the year index varying fastest and the location index varying slowest. For "seasonal" aggregation, the maxes should be provided by month. For seasonal analysis, data should be given as consecutive years, with NA for missing values, as the code needs to treat December as being with the following year. If there are NAs for any months of a given season, the seasonal value is taken to be NA.
  # aggregation should be one of "annual", "seasonal", or "monthly", indicating the stratification. If monthly or seasonal, separate results will be reported for each stratum (i.e., each month or season)
  # nYears is the number of years (more generally of blocks) of data provided
  # nReplicates is the number of replicate data sets; primarily for use with model output where you can run the model multiple times to get independent replicates
  # nCovariates indicates the number of covariates provided through 'covariates' (note that any subset of the covariates that are provided may be used in the location, scale, and shape modeling, as specified in locationModel, scaleModel, shapeModel)
  # covariates is a 1-d array of covariate values (year x covariate x (optionally) month or season), with the observation index varying fastest and month/season index varying slowest. This should be NULL if 'nCovariates' is 0.
  # dataScaling is a positive-valued scalar used to scale the data values for more robust optimization performance. When multiplied by the values, it should produce values with magnitude around 1
  # locationModel, scaleModel, and shapeModel are vectors indicating the indices of the covariates to be used in the location, scale and shape parameterization. The values are used to select columns from the 'covariates' array after they are transformed to multidimensional arrays with the second dimension indexing the covariates
  # missingFlag is a value to be interpreted as missing values (NA in R), intended for use in other languages calling this function
  # returnParams is a boolean indicating whether to return the fitted parameter values and their standard errors; WARNINGS: (1) Parameter values for models with covariates must be interpreted based on transforming each covariate by subtracting the mean of the yearly values (from 'covariatesByYear') and dividing by the difference of the max and min of the yearly values. This scales the covariates for better numerical performance in the optimization. (2) parameter values for models with covariates for the scale parameter must interpreted based on the log transformation of the scale parameter
  # rvInterval: the timespan for which return values should be calculated. For example a rvInterval of 20 years corresponds to the value of an event that occurs with probability 1/20 in any year and therefore occurs on average every 20 years
  # newData should be a 1-d array providing covariate values (observation x covariate) for which return values are desired. Values will be calculated for each stratum.
  # rvDifference should be a 1-d array of covariate values for two sets of covariates for which the difference in return values is desired (set x covariate), with the set index varying fastest; i.e. provide the first covariate for each set, then the second covariate for each set, etc. The difference is computed as the return value for the second set minus the return value for the first set. Values will be calculated for each stratum.
  # maxes should be TRUE when analyzing block maxima and FALSE when analyzing block minima
  # optimMethod allows you to use different methods as specified in 'optim'; by default this is the derivative-free Nelder-Mead method

  require(ismev) # won't be needed when start using gev.fit2

  if(!maxes){  # modeling minima is equivalent to modeling the negative of maxima. Location parameter values will be the negative of those on the original scale, but are corrected before returning parameter values to the user.
    data = -data
  }

  if(!is.null(missingFlag)){
    data[data == missingFlag] <- NA
    if(!is.null(covariates))
     covariates[covariates == missingFlag] <- NA
  }

  nStrata = 1
  if(aggregation == "monthly"){
    nStrata = 12
  }
  if(aggregation == "seasonal"){
    nStrata = 4
  }

  data <- data * dataScaling

  # check dimensionality of input arrays
  if((aggregation == "annual" && length(data) != nYears*nReplicates) ||
     (aggregation != "annual" && length(data) != nYears*12*nReplicates))
    stop("length of input data does not match number of years, covariates, and months (the latter is required for seasonal and monthly analyses")
  if(!is.null(covariates) && !(length(covariates) %in% (nYears*nCovariates*c(1, nStrata))))
    stop("supplied 'covariate' values do not match number of years, covariates and strata")
  if(!is.null(newData) && length(newData) %% nCovariates != 0)
    stop("length of newData should be a multiple of 'nCovariates'")
  if(!is.null(rvDifference) && length(rvDifference) != 2*nCovariates)
    stop("length of rvDifference is not equal to two times the number of covariates")

  # manipulate input arrays to have appropriate number of dimensions
  if(aggregation == "annual"){
    data <- array(data, c(nYears, nStrata, nReplicates))
  } else{
    data <- array(data, c(nYears, 12, nReplicates))
  }
  if(!is.null(covariates))
    covariates <- array(covariates, c(nYears, nCovariates, nStrata))
  if(!is.null(newData)){
    m = length(newData)/nCovariates # number of return values to compute
    newData <- array(newData, c(m, nCovariates))
  }
  if(!is.null(rvDifference))
    rvDifference <- array(rvDifference, c(2, nCovariates))

  if(is.null(rvInterval) && (!is.null(newData) || !is.null(rvDifference)))
    stop("'rvInterval' must be specified")

  if(aggregation == 'seasonal'){
    seasons <- c('DJF', 'MAM', 'JJA', 'SON')
    data <- seasonalize(data, maxes = TRUE)
  }

  nParam <- 3 + length(locationModel) + length(scaleModel) + length(shapeModel)

  if(!is.null(locationModel) && !validateIndices(locationModel, nCovariates))
    stop("'locationModel' values do not provide legitimate indices of covariates")

  if(!is.null(scaleModel) && !validateIndices(scaleModel, nCovariates))
    stop("'scaleModel' values do not provide legitimate indices of covariates")

  if(!is.null(shapeModel) && !validateIndices(shapeModel, nCovariates))
    stop("'shapeModel' values do not provide legitimate indices of covariates")

  if(nCovariates){
    for(p in 1:nCovariates){ # shift and scale to (-.5, .5) for better numeric properties in estimation
      if(!is.null(newData))
        newData[ , p] <- normalize(newData[, p], mean(covariates[ , p, ]), min(covariates[ , p, ]), max(covariates[ , p, ]))
      if(!is.null(rvDifference))
        rvDifference[ , p] <- normalize(rvDifference[, p], mean(covariates[ , p, ]), min(covariates[ , p, ]), max(covariates[ , p, ]))
      covariates[ , p, ] <- normalize(covariates[ , p, ])
    }
  }
  # do I need to save the original covariates or at least mean and divisor of normalization?

  mulink <- siglink <- shlink <- identity
  link = "c(identity, identity, identity)"
  if(!is.null(scaleModel)){
    siglink <- exp
    link = "c(identity, exp, identity)"
  }

  NAlist <- list(mle = rep(NA, nParam), se = rep(NA, nParam), cov = matrix(NA, nParam, nParam))

  gev.fit.wrap <- function(xdat, ydat){
    fit <- try(gev.fit2(xdat, ydat = ydat, mul = locationModel, sigl = scaleModel, shl = shapeModel, mulink = mulink, siglink = siglink, shlink = shlink, show = FALSE))
    if(is(fit, 'try-error') || fit$conv || fit$flag)
      fit <- NAlist
    return(fit)
  }

  extract <- function(index, object, name)
    object[[index]][[name]]

  mle <- se <- array(NA, c(nParam, nStrata))
  covmat <- array(NA, c(nParam, nParam, nStrata))

  for(j in 1:nStrata){
    output <- gev.fit.wrap(c(data[ , j, ]), matrix(rep(c(t(covariates[ , , j])), nReplicates), ncol = nCovariates, byrow = TRUE))
    mle[ , j] <- output[["mle"]]
    if(!maxes)  # location parameters for minima are the negative of those computed based on negative of minima
      mle[1:(length(locationModel)+1), ] <- -mle[1:(length(locationModel)+1), ]
    se[ , j] <- output[["se"]]
    covmat[ , , j] <- output[["cov"]]
  }

  results <- list()
  numLocScaleParams = 2 + length(locationModel) + length(scaleModel)
  # rescale parameters so on scale of original data
  mle[1:numLocScaleParams, ] <- mle[1:numLocScaleParams, ] / dataScaling
  se[1:numLocScaleParams, ] <- se[1:numLocScaleParams, ] / dataScaling

  if(returnParams){
    if(aggregation == 'seasonal')
      attributes(mle)$dimnames[[2]] <- attributes(se)$dimnames[[2]] <- seasons
    results$mle <- mle[ , , drop = TRUE]
    results$se.mle <- se[ , , drop = TRUE]
  }

  # get return values for newData observations
  # perhaps make this more efficient with an apply, but it needs to pass in both mle and cov
  if(!is.null(newData)){
    rv <- array(0, c(m, nStrata, 2))
    for(i in 1:m)
      for(j in 1:nStrata){
        fit = list(mle = mle[ , j], cov = covmat[ , , j], model = list(locationModel, scaleModel, shapeModel), link = link)
        class(fit) = "gev.fit"
        rv[i, j, ] <- returnValue(fit, rvInterval, newData[i, ])
      }
    if(aggregation == "seasonal")
      attributes(rv)$dimnames[[2]] <- seasons
    results$returnValue <- rv[ , , 1]
    results$se.returnValue <- rv[ , , 2]
  }

  # get stationary return value
  if(is.null(newData) && !is.null(rvInterval) && is.null(covariates)){
    rv <- array(0, c(nStrata, 2))
    for(j in 1:nStrata) {
      fit = list(mle = mle[ , j], cov = covmat[ , , j], model = list(locationModel, scaleModel, shapeModel), link = link)
      class(fit) = "gev.fit"
      rv[j, ] <- returnValue(fit, rvInterval, rvCovariates = NULL)
    }
    if(aggregation == "seasonal")
      attributes(rv)$dimnames[[1]] <- seasons
    results$returnValue <- rv[ , 1]
    results$se.returnValue <- rv[ , 2]
  }

  # get return value difference
  if(!is.null(rvDifference)){
    rvDiff <- array(0, c(nStrata, 2))
    for(j in 1:nStrata) {
      fit = list(mle = mle[ , j], cov = covmat[ , , j], model = list(locationModel, scaleModel, shapeModel), link = link)
      class(fit) = "gev.fit"
      rvDiff[j, ] <- returnValueDiff(fit, rvInterval, rvDifference)
    }
    if(aggregation == "seasonal")
      attributes(rvDiff)$dimnames[[1]] <- seasons
    results$returnValueDiff <- rvDiff[ , 1]
    results$se.returnValueDiff <- rvDiff[ , 2]
  }

  return(results)
}