/* tr_sweep.c  94.06.05
 * Copyright 1983-1992   Albert Davis
 * sweep time and simulate.  output results.
 * manage event queue
 */
#include "ecah.h"
#include "branch.h"
#include "error.h"
#include "io.h"
#include "mode.h"
#include "options.h"
#include "status.h"
#include "tr.h"
#include "declare.h"
/*--------------------------------------------------------------------------*/
	void	tr_sweep(transient_t*);
	void	new_event(double);
static	void	tr_first(transient_t*);
static	int	tr_next(transient_t*);
/*--------------------------------------------------------------------------*/
extern       struct ioctrl io;
extern const struct options opt;
extern	     struct status stats;
extern const char e_int[];

extern double trtime0;	/* transient analysis time			    */
extern double genout;	/* output of "signal generator" (ckt input)	    */
extern int sim_phase;	/* simulation phase (init_dc, tran, ...)	    */
extern int bypass_ok;
extern int inc_mode;

static double events[MAXEVENTCOUNT];	/* the event queue		    */
static int nei;		/* next event index				    */
static int eeq;		/* end of event queue				    */
static double nexttick;	/* next pre-scheduled user "event"		    */
/*--------------------------------------------------------------------------*/
void tr_sweep(transient_t *tr)
{
 int converged;
 
 tr_head(tr->start, tr->stop, YES/*linear*/, "Time");
 bypass_ok = NO;
 inc_mode = BAD;
 if (tr->cont){			/* use the data from last time	    */
    trestor();
 }

 tr_first(tr);
 sim_phase = pINIT_DC;
 genout = gen();
 io.suppresserrors = tr->trace < tVERBOSE;
 converged = tr_solve(opt.itl[1],tr->trace);
 if (!converged)
    error(bWARNING, "did not converge\n");
 tr_review(tr);
 tr_out(tr);
 
 while (tr_next(tr)){
    bypass_ok = NO;
    sim_phase = pTRAN;
    genout = gen();
    io.suppresserrors = tr->trace < tVERBOSE;
    converged = tr_solve(opt.itl[4],tr->trace);
    tr_review(tr);
    if (tr->trace >= tREJECTED){
       tr->printnow = YES;
    }else if (!converged  ||  tr->approxtime <= tr->time0){
       tr->printnow = NO;
    }else if (tr->trace >= tALLTIME){
       /* tr->printnow = YES; set in tr_next */
    } /* else (usual case) use the value set in tr_next */
    tr_out(tr);
 }
}
/*--------------------------------------------------------------------------*/
void new_event(double etime)
{
 int ii, jj, kk;

 /*time_start(&(stats.review));*/
 for (ii = nei;  ii != eeq;  ii = (ii+1)%MAXEVENTCOUNT){
    if (etime <= events[ii])
       break;
 }
 for (jj = eeq;  jj != ii;  jj = kk){
    kk = jj - 1;
    if (kk < 0)
       kk += MAXEVENTCOUNT;
    events[jj] = events[kk];
 }
 events[ii] = etime;
 eeq = (eeq+1)%MAXEVENTCOUNT;
 if (eeq == nei)
    error(bERROR, e_int, "event queue overflow");
 if (opt.foooo == 3){
    printf("(%d,%d) ", nei, eeq);
    for (ii = nei;  ii != eeq;  ii = (ii+1)%MAXEVENTCOUNT)
       printf("%g ", events[ii]);
    printf("\n");
 }
 /*time_stop(&(stats.review));*/
}
/*--------------------------------------------------------------------------*/
static void tr_first(transient_t *tr)
{
 /* usually, tr->time0, tr->time1 == 0, from tr_setup */
 time_start(&(stats.review));
 nexttick = tr->time0 + tr->step;		/* set next user step */
 nei = eeq;					/* empty the queue */
 trtime0 = tr->time0;
 tr->printnow = YES;
 stats.control[cSTEPCAUSE] = scUSER;
 ++stats.control[cSTEPS];
 time_stop(&(stats.review));
}
/*--------------------------------------------------------------------------*/
static int tr_next(transient_t *tr)
{
 double newtime;
 
 time_start(&(stats.review));
 /* tr_review(tr); */			/* trunc error, etc. */
 					/* was already done before print */
 
 newtime = nexttick;				/* user time steps */
 stats.control[cSTEPCAUSE] = scUSER;
 
 if (nei != eeq  &&  events[nei] <= newtime){	/* the event queue */
    newtime = events[nei];
    stats.control[cSTEPCAUSE] = scEVENTQ;
 }
 
 if (tr->approxtime < newtime - tr->dtmin){
    if (tr->approxtime < (newtime + tr->time0) / 2.){
       newtime = tr->approxtime;
       stats.control[cSTEPCAUSE] = tr->control;
    }else{
       newtime = (newtime + tr->time0) / 2.;
       stats.control[cSTEPCAUSE] = scHALF;
    }
 }
 
 if (nexttick < newtime + tr->dtmin){		/* advance user time */
    tr->printnow = YES;				/* print if user step */
    fonext();
    nexttick += tr->step;		  /* BUG??? :  does the print get */
 }else{					  /* messed up if a user step is */
    tr->printnow = tr->trace >= tALLTIME; /* rejected?? 		*/
 }
 
 while (nei != eeq  &&  events[nei] <= newtime){/* advance event queue */
    nei = (nei+1)%MAXEVENTCOUNT;
 }
 
 if (newtime < tr->time1 + tr->dtmin){
    error(bDANGER, "very backward time step\n");
    error(bTRACE, "newtime=%e  rejectedtime=%e  oldtime=%e  using=%e\n",
	  newtime, tr->time0, tr->time1, tr->time1 + tr->dtmin);
    newtime = tr->time1 + tr->dtmin;
    stats.control[cSTEPCAUSE] += scNO_ADVANCE;
 }
 if (newtime <= tr->time0 - tr->dtmin){
    error(bLOG, "backwards time step\n");
    error(bLOG, "newtime=%e  rejectedtime=%e  oldtime=%e\n",
	     newtime, tr->time0, tr->time1);
    stats.control[cSTEPCAUSE] += scREJECT;
    if (newtime < (nexttick - tr->step)){
       error(bLOG, "user step rejected\n");
       nexttick -= tr->step;
    }
 }else if (newtime < tr->time0 + tr->dtmin){
    error(bWARNING, "zero time step\n");
    error(bLOG, "newtime=%e  rejectedtime=%e  oldtime=%e\n",
	     newtime, tr->time0, tr->time1);
    tr->time1 = tr->time0;
    newtime = tr->time0 + tr->dtmin;
    stats.control[cSTEPCAUSE] += scZERO;
 }else{
    tr->time1 = tr->time0;
 }
 
 trtime0 = tr->time0 = newtime;
 ++stats.control[cSTEPS];
 time_stop(&(stats.review));
 return (tr->time0 <= tr->stop + tr->dtmin);
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
