//
// Copyright 2003, Juan Segarra Flor
//
// This is part of videocache
//
// Videocache is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Videocache is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//

#include <iostream>
#include <math.h>

#include "config.h"
#include "cachetree.h"
#include "videos.h"
#include "bwtable.h"
#include "distr/Random.h"

#define REQPERSLOT (REQPERMIN*60/TIMESLOT)

/********************** disclaimer *******************/
class notice {
 public:
  notice() {cerr<<endl<<"    Videocache revision 1.36, Copyright (C) 2003 Juan Segarra Flor"<<endl
		    <<"    Videocache comes with ABSOLUTELY NO WARRANTY; for details see 'README'."<<endl
		    <<"    This is free software, and you are welcome to redistribute it"<<endl
		    <<"    under certain conditions; see 'COPYING' for details."<<endl<<endl;}
};
notice GNUnotice;

/******************** global objects *****************/
cachetree tree(TREE_DEPTH,CHILDREN_EACH_NODE,USERS_EACH_NODE);
videos videorep(NUMVIDEOS);
bwtable table(tree.getmaxlinks(),LINK_BW,NUMVIDEOS,(long)(pow(CHILDREN_EACH_NODE,TREE_DEPTH-1)*USERS_EACH_NODE),USERLINK_BW);

UniformStream myrand(0,1);

/***************** global result variables ************/
long partstransmittedpp=0; //parts transmited from point to point
long partsmulticastpp=0;  // parts transmited without bw cost pp
long requested=0; // videos requested
long acceptedv=0; // videos accepted for delivery
long rejectedv=0; // videos requested and rejected
char transmittedvideos[NUMVIDEOS]; // 1=transmitted, 0=not transmitted
long *linktraffic; // array storing the traffic in each link
int lastslot; // last slot used
long storedparts[TREE_DEPTH]; // number of parts stored in each level
long storedspace[TREE_DEPTH]; // space used in each level (Mb)
double meandelay;  // mean start delay of a transmission
double maxdelay=0;// max start delay of a transmission
double meantries=0;  // mean tries for allocating a transmission
double probreqlevel[TREE_DEPTH]; // prob of request of videos in each level

/****************** other global **********************/
long maxusers=(long)(pow(CHILDREN_EACH_NODE,TREE_DEPTH-1)*USERS_EACH_NODE);
double MAXOUTPUT;  // max output of the system (videos/minute)
#define CONGESTED 99  // percentage for raising the flag
char mainlinkcongestionflag=0;  // whether the main links are congested

// video reallocation
int disabledhits=0; // n. request of video parts currently disabled
int disabledtime=0; // total time where some video is disabled
int* videobusy=NULL;  // used by enablevideos(),disable(),vidbusyuntil()
int numvideosbusy=0;  // used by enablevideos(),disable(),vidbusyuntil()

/****************** function prototypes **************/
void requestvideo(double time,int numslot,long user,int *busyuntil);
int nextbatchslot(int numslot,double time,int video);
void drawpoints(long max);
void getnextrequest(double &time,long &numuser,long maxusers,
		    int *busyuntil);
double timemodifier(double time);
void showresults(double time);
void showsystem(void);
int makerequest(int video,long user,int startslot,int *busyuntil);
void calclinkutilization(long *now);
double getmaxoutput();  //calculates MAXOUTPUT
void setglobalvars(); // sets probreqlevel and MAXOUTPUT

void disable(int video,int part,int from,int until);// disable a video part
int enablevideos(int slot); // searches and enables disabled videos
// returns the number of remaining disabled video parts
int isvidbusy(int video,int part,int from,int until);
 // returns if a video part is avail
int reallocatevideo(int slot);
int reallocatevideopart(int video,int part,int level,int startslot);
int* remember(int video,int part,int level,int*remembered,int&numremembered);
int* reallremembered(int level,int start,int&maxcompletionslot,int*remembered,int&numremembered);

/************************* main ***********************/
int main() {
  int userbusy[maxusers];
  double time=0; // simulation time
  int prevtime=0; // previous time
  int slot; // slot for the requested video
  long c1;
  long user; // id of the user which requests a video

  /* var initialization */
  for (c1=0;c1<NUMVIDEOS;c1++)
    transmittedvideos[c1]=0;
  for (c1=0;c1<maxusers;c1++)
    userbusy[c1]=-1;
  for (c1=0;c1<TREE_DEPTH;c1++){ 
    storedparts[c1]=0;
    probreqlevel[c1]=0;
  }
  linktraffic=(long*)calloc(tree.getmaxlinks(),sizeof(long));
  if (linktraffic==NULL) {
    cerr<<"Not enough memory for storing traffic information"<<endl;
    exit(-1);
  }
  for (c1=0;c1<tree.getmaxlinks();c1++)
    linktraffic[c1]=0;

  /* system setup */
  //tree.printtree();
  //tree.printlinks();
  videorep.distribute(tree);
  //videorep.printvideos();

  /* var initialization on the configured system */
  setglobalvars();

  /* program */
  showsystem();

  cerr<<"Running program..."<<endl;
  //slot=(int)(ceil(time))+REQPROCESSTIME;
  slot=REQPROCESSTIME;
  table.setactualslot(slot);
  while (time<MAXTIME) {
    getnextrequest(time,user,maxusers,userbusy);
    while (time>=prevtime) {
#ifdef SENDTOFILLLINK
      if (MAXTHRESHOLD>=MINTHRESHOLD) {
	if ((((int)rint(time)%1440)>=MINTHRESHOLD) && 
	    (((int)rint(time)%1440)<MAXTHRESHOLD))
	  table.adjustthisslot(prevtime+REQPROCESSTIME);
      }
      else if ((((int)rint(time)%1440)>=MINTHRESHOLD) || 
	       (((int)rint(time)%1440)<MAXTHRESHOLD))
	table.adjustthisslot(prevtime+REQPROCESSTIME);
#endif
      slot++;
      table.setactualslot(slot);
      table.delslotsto(prevtime);
      calclinkutilization(linktraffic);
      drawpoints(MAXTIME);
      prevtime++;
#ifdef REALLMETHOD
#warning Videos should be identical 
      // because videos are not currently moved, but they movement 
      // transmissions are planned. This should statistically offer
      // the same results, although it's not totally correct.
      if ((slot%1440)==60)  // try to reserve it asap
	  reallocatevideo(slot+REDISTRTIME-60);
      if (enablevideos(slot)) {
	disabledtime++;
      }
#endif
    }
    //cerr<<"requesting ("<<user<<")... "<<flush;
    requestvideo(time,slot,user,userbusy);
    //cerr<<"done"<<endl;
  }
  cerr<<"Processing results... "<<flush;
  lastslot=slot;
  showresults(time);
  cerr<<"done"<<endl;
  return 0;
}

/* shows results at the end of the simulation */
void showresults(double time) {
  long transmittedv=0,c1;
  
  cout<<endl<<" * * * * * * * * RESULTS * * * * * * * *"<<endl;
  cout<<"Simulated time: "<<(int)(floor(time)+1)<<" slots ("
      <<(int)(time*TIMESLOT/3600)<<" hr "
      <<(int)((time*TIMESLOT)/60)%60<<" min "
      <<(int)(time*TIMESLOT)%60<<" sec)"<<endl;
  cout<<"Planned time: "<<lastslot<<" slots ("
      <<lastslot*TIMESLOT/3600<<" hr "
      <<(lastslot*TIMESLOT/60)%60<<" min "
      <<(lastslot*TIMESLOT)%60<<" sec)"<<endl;
  cout<<"Requests: "<<requested<<endl;
  cout<<"Accepted videos: "<<acceptedv<<endl;
  cout<<"Rejected videos: "<<rejectedv<<endl;
  if (partstransmittedpp!=0) {
    cout<<"Transmitted parts added to multicast (without bw costs): "
	<<partsmulticastpp<<" ("<<partsmulticastpp*100/partstransmittedpp
	<<"%)"<<endl;
    cout<<"Mean transmissions in each multicast: "
	<<(double)partstransmittedpp/(partstransmittedpp-partsmulticastpp)<<endl;
  }
#ifdef REALLMETHOD
  cout<<"Time with some disabled video part: "<<disabledtime<<" slots ("
    //<<(int)(disabledtime*TIMESLOT/3600)<<" hr "
      <<(int)(((disabledtime/(floor(time)/1440))*TIMESLOT)/60)<<" min each day)"
    //<<(int)(disabledtime*TIMESLOT)%60<<" sec), "
    //<<", "<<(double)disabledtime*100/(floor(time)+1)<<"% of time"
      <<endl;
  cout<<"Number of disabled video parts requested: "<<disabledhits<<endl;
#endif
  for (c1=0;c1<NUMVIDEOS;c1++)
    if (transmittedvideos[c1]==1)
      transmittedv++;
  cout<<"Different videos requested: "<<transmittedv<<" ("
      <<transmittedv*100/NUMVIDEOS<<"%)"<<endl;
  cout<<"Mean delay in acceptance: "<<(int)(meandelay*TIMESLOT)<<" s ("
      <<(int)(meandelay*TIMESLOT/3600)<<" hr "
      <<(int)(meandelay*TIMESLOT/60)%60<<" min "
      <<(int)(meandelay*TIMESLOT)%60<<" sec)"<<endl;
  cout<<"Max. delay in acceptance: "<<(int)(maxdelay*TIMESLOT)<<" s ("
      <<(int)(maxdelay*TIMESLOT/3600)<<" hr "
      <<(int)(maxdelay*TIMESLOT/60)%60<<" min "
      <<(int)(maxdelay*TIMESLOT)%60<<" sec)"<<endl;
  cout<<"Mean tries in allocation: "<<meantries<<endl;
}

/* places a request at $time$ for $user$ */
void requestvideo(double time,int numslot,long user,int *busyuntil) {
  double popularity;  // popularity of the video
  int video;
  int startslot;//,partstart;
  //int numslot;
  double delay;
  int numbertries;
  char acceptedflag,tryagain=1;

  requested++;

  while (tryagain) {
    popularity=myrand();
    video=videorep.getvideobypopularity(popularity);
    startslot=-1;//table.getfirsttranstime(video);
    numbertries=1;
    if (startslot==-1) {
      startslot=nextbatchslot(numslot,time,video);
    } else if (startslot<numslot) {
      cerr<<"videocache: startslot ("<<startslot<<" lower than actual slot ("<<numslot<<")"<<endl;
      exit(-2);
    }
    acceptedflag=makerequest(video,user,startslot,busyuntil);
    while ((acceptedflag==0)&&(startslot-time<=MAX_ACCEPTED_DELAY)) {
      startslot++;
      numbertries++;
      acceptedflag=makerequest(video,user,startslot,busyuntil);
    }
    if (acceptedflag)
      tryagain=0;
    else
#ifdef SHOWDELAYTOOLONG
      cout<<">>videoreselection: video "<<video<<endl;
#endif
  }

#ifdef SHOWBATCH
    cout<<">>batch "<<time<<" "<<startslot-numslot<<endl;
#endif
#ifdef SHOWDELAYS
    cout<<">>delay "<<time<<" "<<startslot-time<<endl;
#endif
  meantries=(meantries*(requested-1)+numbertries)/requested;
  delay=startslot-time;
  meandelay=(meandelay*(requested-1)+delay)/requested;
  if (maxdelay<delay)
    maxdelay=delay;
  acceptedv++;
}

/* concrete video request */
/*
int makerequest(int video,long user,int startslot,int*busyuntil) {
  int parts,part;
  int videoaccepted=1;
  int *route,*sources,duration,partstart,totalduration;
  long rate;
  int flvideobusy;

  parts=videorep.getnumparts(video);
  partstart=startslot;
  //cerr<<"req v"<<video<<" slt"<<partstart<<" usr"<<user<<endl;
  for (part=0;part<parts;part++) {
    if (videoaccepted) {
      duration=(videorep.getduration(video,part)/TIMESLOT);
      rate=videorep.getrate(video,part);
      flvideobusy=isvidbusy(video,part,partstart,partstart+duration);
      if (flvideobusy==0) 
	sources=videorep.getlocation(video,part);
      else {
	sources=(int*)calloc(2,sizeof(int));
	sources[0]=1;// main server
	sources[1]=-1;
      }
      route=tree.getbestroute(sources,user);
      free(sources);
      videoaccepted=table.testroute(video,part,rate,partstart,duration,route,user);
      partstart+=duration;
      free(route);
    }
  }
  if (videoaccepted) {
    transmittedvideos[video-1]=1; // mark video as transmitted
    partstart=startslot;
    totalduration=0;
    for (part=0;part<parts;part++) {
      duration=(videorep.getduration(video,part)/TIMESLOT);
      totalduration+=duration;
      rate=videorep.getrate(video,part);
      flvideobusy=isvidbusy(video,part,partstart,partstart+duration);
      if (flvideobusy==0) 
	sources=videorep.getlocation(video,part);
      else {
	sources=(int*)calloc(2,sizeof(int));
	sources[0]=1;// main server
	sources[1]=-1;
	disabledhits++;
      }
      route=tree.getbestroute(sources,user);
      free(sources);
      //cerr<<"resv v"<<video<<" p"<<part<<" bw"<<rate<<" slt"<<partstart<<" dur"<<duration<<" usr"<<user<<endl;
      if (!table.reserveroute(video,part,rate,partstart,duration,route,user)) {
	cerr<<"A reservation has failed when but its test was accepted"<<endl;
	exit(-2);
      }
      partstart+=duration;
      free(route);
    }
    busyuntil[user]=startslot+totalduration;// mark user as busy
  }
  return videoaccepted;
}
*/
int makerequest(int video,long user,int startslot,int*busyuntil) {
  int parts,part;
  int videoaccepted=1,undo=0,undopart;
  int *route,*sources,duration,partstart,totalduration;
  long rate;
  int flvideobusy;
  int *partroute[100];

  parts=videorep.getnumparts(video);
  partstart=startslot;
  // test the transmission of each video part
  for (part=0;part<parts;part++) {
    if (videoaccepted) {
      duration=(videorep.getduration(video,part)/TIMESLOT);
      rate=videorep.getrate(video,part);
      flvideobusy=isvidbusy(video,part,partstart,partstart+duration);
      if (flvideobusy==0) 
	sources=videorep.getlocation(video,part);
      else {
	sources=(int*)calloc(2,sizeof(int));
	sources[0]=1;// main server
	sources[1]=-1;
      }
      route=tree.getbestroute(sources,user);
      free(sources);
      videoaccepted=table.testroute(video,part,rate,partstart,duration,route,user);
      partstart+=duration;
      free(route);
    }
  }
  if (videoaccepted) {
    //cerr<<"resv v"<<video<<" usr"<<user<<endl;
    partstart=startslot;
    totalduration=0;
    part=0;
    while ((!undo)&&(part<parts)) {
      //cerr<<" part "<<part<<flush;
      duration=(videorep.getduration(video,part)/TIMESLOT);
      totalduration+=duration;
      rate=videorep.getrate(video,part);
      flvideobusy=isvidbusy(video,part,partstart,partstart+duration);
      if (flvideobusy==0) 
	sources=videorep.getlocation(video,part);
      else {
	sources=(int*)calloc(2,sizeof(int));
	sources[0]=1;// main server
	sources[1]=-1;
	disabledhits++;
      }
      route=tree.getbestroute(sources,user);
      free(sources);
      //cerr<<"resv v"<<video<<" p"<<part<<" bw"<<rate<<" slt"<<partstart<<" dur"<<duration<<" usr"<<user<<endl;
      if (table.testroute(video,part,rate,partstart,duration,route,user)){
	if (!table.reserveroute(video,part,rate,partstart,duration,route,user)) {
	  cerr<<"A reservation has failed when but its test was accepted"<<endl;
	  exit(-2);
	}
	//cerr<<" ok"<<endl;
      }
      else {
	//cerr<<" failed"<<endl;
	undo=1;
      }
      partstart+=duration;
      partroute[part]=route;
      //free(route);
      part++;
    }
    if (!undo) {
      busyuntil[user]=startslot+totalduration;// mark user as busy
      transmittedvideos[video-1]=1; // mark video as transmitted
    }
    else {
      //cerr<<"failed reservation detected at part "<<part-1<<endl;
      videoaccepted=0;
      part--;
      partstart=startslot;
      totalduration=0;
      for (undopart=0;undopart<part;undopart++) {
	//cerr<<"unreserving part "<<undopart<<endl;
	duration=(videorep.getduration(video,undopart)/TIMESLOT);
	totalduration+=duration;
	rate=videorep.getrate(video,undopart);
	flvideobusy=isvidbusy(video,undopart,partstart,partstart+duration);
	if (flvideobusy) 
	  disabledhits--;
	if (!table.unreserveroute(video,undopart,rate,partstart,duration,partroute[undopart],user)) {
	  cerr<<"makerequest: error unreserving transmission"<<endl;
	  exit(-2);
	}  
      }
    }
  }
  for (undopart=0;part<part;undopart++)
    free(partroute[undopart]);
  return videoaccepted;
}


void disable(int video,int part,int from,int until){
  int* auxvidbusy;

  //if (vidbusyuntil(video,part)>=until) {
  //  cerr<<"disable: Trying to disable an already disabled video part"<<endl;
  //  exit(-2);
  //}
  //cerr<<"disable v"<<video<<" p"<<part<<" fr"<<from<<" tl"<<until<<endl;
  auxvidbusy=(int*)calloc(4*(numvideosbusy+1),sizeof(int));
  memcpy(auxvidbusy,videobusy,4*numvideosbusy*sizeof(int));
  //cerr<<"cmpy"<<endl;
  auxvidbusy[4*numvideosbusy]=video;
  auxvidbusy[4*numvideosbusy+1]=part;
  auxvidbusy[4*numvideosbusy+2]=from;
  auxvidbusy[4*numvideosbusy+3]=until;
  numvideosbusy++;
  free(videobusy);
  videobusy=auxvidbusy;
}
  

int enablevideos(int slot){
  int c1;
  int* auxvidbusy;
  char anydisabled=0;

  //cerr<<"en"<<endl;
  c1=0;
  while (c1<numvideosbusy) {
    if (videobusy[4*c1+3]<=slot) {
      auxvidbusy=(int*)calloc(4*(numvideosbusy-1),sizeof(int));
      memcpy(auxvidbusy,videobusy,4*c1*sizeof(int));
      memcpy(&auxvidbusy[4*c1],&videobusy[4*(c1+1)],
	     4*(numvideosbusy-c1-1)*sizeof(int));
      free(videobusy);
      videobusy=auxvidbusy;
      numvideosbusy--;
    }
    else {
      if (videobusy[4*c1+2]<=slot) {
	anydisabled=1;
	//cerr<<"slot "<<slot<<": video "<<videobusy[4*c1]<<" part "<<videobusy[4*c1+1]<<" will be still busy from "<<videobusy[4*c1+2]<<" to "<<videobusy[4*c1+3]<<" slots"<<endl;
      }
      c1++;
    }
  }
  return anydisabled;
}

// returns 0 otherwise
int isvidbusy(int video,int part,int from,int until) {
  int c1;
  char notfound=1;
  
  //cerr<<"vbu"<<endl;
  c1=0;
  while (notfound && (c1<numvideosbusy)) {
    if ((videobusy[4*c1]==video) && (videobusy[4*c1+1]==part)) {
      if (((videobusy[4*c1+2]<=from)&&(videobusy[4*c1+3]>=from))||
	  ((videobusy[4*c1+2]<=until)&&(videobusy[4*c1+3]>=until)))
	//(videobusy[4*c1+2]>until) && (videobusy[4*c1+3<from]))
	notfound=0;
      else
	c1++;
    }
    else
      c1++;
  }
  if (notfound)
    return 0;
  else
    return 1;
}

#ifdef REALLMETHOD
int* remember(int video,int part,int level,int*remembered,int&numremembered){
  int* auxremembered;

  //if (vidbusyuntil(video,part)!=-1) {
  //  cerr<<"disable: Trying to disable an already disabled video part"<<endl;
  //  exit(-2);
  //}
  auxremembered=(int*)calloc(3*(numremembered+1),sizeof(int));
  memcpy(auxremembered,remembered,3*numremembered*sizeof(int));
  //cerr<<"cmpy"<<endl;
  auxremembered[3*numremembered]=video;
  auxremembered[3*numremembered+1]=part;
  auxremembered[3*numremembered+2]=level;
  numremembered++;
  free(remembered);
  //videobusy=auxvidbusy;
  return auxremembered;
}

int* reallremembered(int level,int startslot,int&maxcompletionslot,int*remembered,int &numremembered) {
  int c1;
  int* auxremembered=NULL;
  int completionslot,videoaux,partaux;
  
  c1=0;
  maxcompletionslot=startslot;
  while (c1<numremembered) {
    if (remembered[3*c1+2]==level) {
      videoaux=remembered[3*c1];
      partaux=remembered[3*c1+1];
      //cerr<<"st sl (lvl"<<level<<"): "<<startslot;
      completionslot=reallocatevideopart(videoaux,partaux,level,startslot);
      //cerr<<" cmp sl "<<completionslot<<endl;
      disable(videoaux,partaux,startslot,completionslot);
      auxremembered=(int*)calloc(3*(numremembered-1),sizeof(int));
      memcpy(auxremembered,remembered,3*c1*sizeof(int));
      memcpy(&auxremembered[3*c1],&remembered[3*(c1+1)],
	     3*(numremembered-c1-1)*sizeof(int));
      free(remembered);
      remembered=auxremembered;
      numremembered--;
      if (completionslot>maxcompletionslot)
	maxcompletionslot=completionslot;
    }
    else {
      c1++;
    }
  }
  return auxremembered;
}

// returns 1 if that video part is busy during those slots

 /* adds a video with max. popularity and redistributes the system */
int reallocatevideo(int slot){
  int video=NUMVIDEOS; // we will remove the last one an use it
  // as the new added video
  int part,parts,c1;
  int videoaux,partaux;
  int size=0,sizeofparts; //MB
  int completionslot,maxcomplslotinlevel,startslot;

  char reallast; // if videos are CBR is not necessary a redistribution

  int* remembered=NULL;
  int numremembered=0;

  // calculate the needed size
  size=sizefunction(TREE_DEPTH,2)-storedspace[1];// free space
  parts=videorep.getnumparts(video);
  completionslot=slot;
  startslot=slot;
  for (part=0;part<parts;part++) {
    size+=videorep.getpartsize(video,part);
  // calculate parts which should be redistributed
  //maxcomplslotinlevel=slot;
    if (REALLMETHOD==DOWNSTREAM) {
      completionslot=reallocatevideopart(video,part,2,startslot);
      disable(video,part,startslot,completionslot);
    }
    else if (REALLMETHOD==UPSTREAM) {
      // remember videos to be realocated
      remembered=remember(video,part,2,remembered,numremembered);
    }
    else {
      cerr<<"Reallocation method unknown"<<endl;
      exit(-2);
    }
  }

  startslot=slot;
  for (c1=2;c1<TREE_DEPTH;c1++) { // level 1 is user level
    sizeofparts=sizefunction(TREE_DEPTH,c1)-storedspace[c1-1];
    //startslot=maxcomplslotinlevel;
    reallast=1;
    while (sizeofparts<size) {
      if (reallast) {
	videorep.getlastvideopartinlevel(c1,tree,videoaux,partaux);
	reallast=0;
      }
      else {
	if (partaux!=0)
	  partaux--;
	else {
	  videoaux--;
	  partaux=videorep.getnumparts(videoaux)-1;
	}
      }
      sizeofparts+=videorep.getpartsize(videoaux,partaux);
      if (c1<TREE_DEPTH-1) {
	if (REALLMETHOD==DOWNSTREAM) {
	  completionslot=reallocatevideopart(videoaux,partaux,c1+1,startslot);
	  disable(videoaux,partaux,startslot,completionslot);
	}
	else if (REALLMETHOD==UPSTREAM) {
	  // remember videos to be realocated
	  remembered=remember(videoaux,partaux,c1+1,remembered,numremembered);
	}
	else {
	  cerr<<"Reallocation method unknown"<<endl;
	  exit(-2);
	}
      }
    }
    size=sizeofparts-(sizefunction(TREE_DEPTH,c1)-storedspace[c1-1]);
  }
  if (REALLMETHOD==UPSTREAM) {
    maxcomplslotinlevel=slot;    
    for (c1=TREE_DEPTH-1;c1>1;c1--) {
      startslot=maxcomplslotinlevel;
      remembered=reallremembered(c1,startslot,completionslot,remembered,numremembered);
      //cerr<<"lvl"<<c1<<" st"<<startslot<<" cmp"<<completionslot<<endl;
      // reallocatevideopart and disable inside reallremembered
      if (completionslot>maxcomplslotinlevel)
	maxcomplslotinlevel=completionslot;
    }
  }
  free(remembered);
  // change popularities // not necessary if CBR
  return 1;
}

// returns the accepted starslot of the transmission
int reallocatevideopart(int video,int part,int level,int startslot) {
  int videoaccepted=0;
  int *route=NULL,*sources=NULL,duration,slot=startslot;
  long rate;
  int c1,csrc,crte;
  int numlinks;

  // reserve the transmission
  duration=(videorep.getduration(video,part)/TIMESLOT);
  rate=videorep.getrate(video,part);

  if ((REALLMETHOD==DOWNSTREAM)||
      ((REALLMETHOD==UPSTREAM)&&(level==2))) {
    numlinks=0;
    for (c1=1;c1<=(TREE_DEPTH-level);c1++)
      numlinks+=(int)(pow(CHILDREN_EACH_NODE,c1));
    route=(int*)calloc(numlinks+1,sizeof(int));
    crte=0;
    for (c1=TREE_DEPTH-1;c1>=level;c1--){
      sources=tree.getcachesinlevel(c1);
      csrc=0;
      while (sources[csrc]!=-1)
	route[crte++]=tree.getlinknodeparent(sources[csrc++]);
    }
    route[numlinks]=-1;
    //cerr<<"st: "<<startslot<<endl;
    free(sources);
  }
  else if (REALLMETHOD==UPSTREAM) {
    sources=videorep.getlocation(video,part);
    numlinks=(int)(pow(CHILDREN_EACH_NODE,TREE_DEPTH-level));
    // only ones from a side
    if (numlinks%CHILDREN_EACH_NODE!=0) {
      cerr<<"numlinks must be multiple of CHILDREN_EACH_NODE"<<endl;
      exit(-2);
    }
    route=(int*)calloc(numlinks+1,sizeof(int));
    c1=0;
    while (sources[c1*CHILDREN_EACH_NODE]!=-1) {
      route[c1]=tree.getlinknodeparent(sources[c1*CHILDREN_EACH_NODE]);
      c1++;
    }
    route[numlinks]=-1;
    free(sources);
  }
  else {
    cerr<<"Reallocation method unknown"<<endl;
    exit(-2);
  }

  while (!videoaccepted) {
    videoaccepted=table.testroute(video,part,rate,slot,duration,route,-1);
    slot++;
  }
  slot--;
  // -1 means (should mean) the transmission plan will not be moved
  if (!table.reserveroute(video,part,rate,slot,duration,route,-1)) {
    cerr<<"A reservation has failed but its test was accepted"<<endl;
    exit(-2);
  }
  free(route);
  return slot+duration;
}
#endif

/* obtains the next time for a request and the user who does it */
void getnextrequest(double &time,long &numuser,long maxusers,
		    int *busyuntil) {
  double reqperslot;

  //reqperslot=REQPERSLOT;
  reqperslot=(double)(REQPERDAY)/86400*TIMESLOT*timemodifier(time);
  //cout<<"rps: "<<reqperslot<<endl;
  time+=-(1/(reqperslot*maxusers))*log(myrand());
#ifdef SHOWSTATISTICALREQ
  cout<<">>statreq "<<time<<endl;
#endif
  numuser=(long)(myrand()*maxusers);
  while (busyuntil[numuser]>=(int)floor(time)) {
    reqperslot=(double)(REQPERDAY)/86400*TIMESLOT*timemodifier(time);
    time+=-(1/(reqperslot*maxusers))*log(myrand());
#ifdef SHOWSTATISTICALREQ
  cout<<">>statreq "<<time<<endl;
#endif
    numuser=(long)(myrand()*maxusers);
  }
}
double timemodifier(double time) {
  int timeofday;
  timeofday=(int)((long)(time)%1440); // slot (minute) of day
  if (timeofday<(1080)) //18*60
    return 0.696;//2*0.34782609 aprox
  else if (timeofday<(1260)) // 21*60
    //return 0.696+2.4*((timeofday-18*60)/(3*60));
    return 0.696+2.4347826*(((double)timeofday-1080)/180);
  else
    //return 0.696+2.4*(1-((timeofday-21*60)/(3*60)));
    return 0.696+2.4347826*(1-(((double)timeofday-1260)/180));
}

/* shows the configuration of the system */
void showsystem(void) {
  int c1;

  cout<<"$Date: 2003/06/25 07:24:21 $"<<endl<<"$Revision: 1.36 $"<<endl<<"$Author: jflor $"<<endl;

  cout<<endl<<" * * * * * SYSTEM INFORMATION * * * * *"<<endl;
  cout<<"Tree depth: "<<TREE_DEPTH<<endl;
  cout<<"Children/node: "<<CHILDREN_EACH_NODE<<endl;
  cout<<"Users at each final node: "<<USERS_EACH_NODE<<endl;
  cout<<"Total users: "<<maxusers<<endl;
  cout<<"Link bandwidth: "<<LINK_BW<<" kbps"<<endl;
  cout<<"Timeslot: "<<TIMESLOT/60<<" min "
      <<TIMESLOT%60<<" sec"<<endl;
  cout<<endl<<" * * * * * VIDEOS INFORMATION * * * * *"<<endl;
  cout<<"Videos available: "<<NUMVIDEOS<<endl;
  cout<<"Used Traces: "<<MAXTRACES<<endl;
  cout<<"Mean video rate: "<<videorep.getmeanrate()<<" kbps"<<endl;
  cout<<"Mean parts each video: "<<videorep.getmeanparts()<<endl;
  cout<<"Mean video duration: "<<videorep.getmeanduration()<<" s ("
      <<videorep.getmeanduration()/60<<" min "
      <<videorep.getmeanduration()%60<<" sec)"<<endl;
  cout<<"Mean part duration: "
      <<videorep.getmeanduration()/videorep.getmeanparts()<<" s ("
      <<(int)(videorep.getmeanduration()/videorep.getmeanparts())/60<<" min "
      <<(int)(videorep.getmeanduration()/videorep.getmeanparts())%60<<" sec)"<<endl;
  cout<<endl<<" * * *  DISTRIBUTION INFORMATION  * * *"<<endl;
  for (c1=1;c1<TREE_DEPTH;c1++)
    cout<<"Parts stored at level "<<c1<<": "<<storedparts[c1]
	<<" ("<<storedspace[c1]<<" of "
	<<sizefunction(TREE_DEPTH,c1+1)<<" Mb; "
	<<(double)storedspace[c1]*100/sizefunction(TREE_DEPTH,c1+1)
	<<"%)"<<endl;
  for (c1=1;c1<TREE_DEPTH;c1++)
    cout<<"Prob. of request of videos at level "<<c1<<": "<<probreqlevel[c1]<<endl;
  cout<<endl<<" * * * *  BATCHING INFORMATION  * * * *"<<endl;
#ifdef DYNAMICBATCH
  cout<<"Dynamic Batchtime"<<endl;
  cout<<" Future time: "<<BATCHFUTUREREF<<" timeslots ("
      <<BATCHFUTUREREF*TIMESLOT/60<<" min "
      <<(BATCHFUTUREREF*TIMESLOT)%60<<" sec)"<<endl;
  MAXOUTPUT=getmaxoutput();
  cout<<" Max. output: "<<MAXOUTPUT<<" videos/timeslot"<<endl;
#else
  cout<<"Constant Batchtime: "<<(BATCHTIME*TIMESLOT)/60<<" min "
      <<(BATCHTIME*TIMESLOT)%60<<" sec"<<endl;
#endif
#ifdef SENDTOFILLLINK
  cout<<endl<<" * * * *  DYNAMIC LINK FILLING  * * * *"<<endl;
  cout<<"Max. video bandwidth: "<<MAXDISKBW<<" kbps"<<endl;
  cout<<"Activation time: "<<MINTHRESHOLD<<" min ("
      <<MINTHRESHOLD/60<<" hours "
      <<MINTHRESHOLD%60<<" min)"<<endl;
  cout<<"Deactivation time: "<<MAXTHRESHOLD<<" min ("
      <<MAXTHRESHOLD/60<<" hours "
      <<MAXTHRESHOLD%60<<" min)"<<endl;
  cout<<"Link filling algorithm: ";
  if (SENDALGORITHM==UNPOPULARFIRST) 
    cout<<"Unpopular videos first"<<endl;
  else if (SENDALGORITHM==POPULARFIRST) 
    cout<<"Popular videos first"<<endl;
  else
    cout<<"Unknown"<<endl;
  cout<<"Part sending selection: ";
  if (PARTSENDALGORITHM==LATESTPARTSFIRST)
    cout<<"Latest parts first"<<endl;
  else if (PARTSENDALGORITHM==BALANCETRANSM)
    cout<<"Balance video"<<endl;
  else if (PARTSENDALGORITHM==BALANCELINKS)
    cout<<"Balance links"<<endl;
  else if (PARTSENDALGORITHM==FIRSTPARTSFIRST)
    cout<<"First parts first"<<endl;
  else
    cout<<"Unknown"<<endl;
#endif
#ifdef REALLMETHOD
  cout<<endl<<" * * * * VIDEO REDISTRIBUTION * * * * *"<<endl;
  cout<<"Redistribution time: "<<REDISTRTIME<<" timeslot(s)"<<endl;
  cout<<"Redistribution method: ";
  if (REALLMETHOD==DOWNSTREAM)
    cout<<"DownStream"<<endl;
  else if (REALLMETHOD==UPSTREAM)
    cout<<"UpStream"<<endl;
  else
    cout<<"Unknown"<<endl;
#endif
  cout<<endl<<" * * * * * OTHER INFORMATION  * * * * *"<<endl;
  cout<<"Max. time: "<<MAXTIME<<" min ("<<
    MAXTIME/60<<" hr "<<
    MAXTIME%60<<" min)"<<endl;

  cout<<"Prob. req. per day: "<<REQPERDAY<<endl;
  cout<<"Request processing time: "<<REQPROCESSTIME<<" timeslot(s)"<<endl;
  cout<<"Max. popularity: "<<videorep.getprobofrequest(1)<<endl;
  cout<<"Min. popularity: "<<videorep.getprobofrequest(NUMVIDEOS)<<endl;
#ifdef MULTICAST  
  cout<<"Multicast transmissions ALLOWED"<<endl;
#else
  cout<<"Multicast transmission NOT ALLOWED"<<endl;
#endif
#ifdef STREAMMERGING
  cout<<"Stream merging ALLOWED"<<endl;
#else
  cout<<"Stream merging NOT ALLOWED"<<endl;
#endif
  cout<<"Distribution algorithm: ";
  if (FILLALGORITHM==WEIGHTEDPARTS)
    cout<<"WeightedParts";
  else if (FILLALGORITHM==WEIGHTEDVIDEOS)
    cout<<"WeightedVideos";
  else if (FILLALGORITHM==WEIGHTEDRESERVE)
    cout<<"WeightedReserve";
  else if (FILLALGORITHM==WEIGHTEDRESERVE2)
    cout<<"WeightedReserve 2";
  else
    cout<<"Unknown";
  cout<<endl;
}


/* calculates the link utilization each timeslot */
void calclinkutilization(long *now) {
  int c1;
  double leveltraffic[TREE_DEPTH];
  int linksatlevel[TREE_DEPTH];

  for (c1=0;c1<TREE_DEPTH;c1++) {
    leveltraffic[c1]=0;
    linksatlevel[c1]=0;
  }
  for (c1=0;c1<tree.getmaxlinks();c1++) {
    leveltraffic[tree.getlinkdepth(c1)]+=(double)linktraffic[c1];
    linksatlevel[tree.getlinkdepth(c1)]++;
  }
  if (((leveltraffic[TREE_DEPTH-1]/linksatlevel[TREE_DEPTH-1])
      *100/table.getlinkcapacity(TREE_DEPTH-1))>=CONGESTED)
    mainlinkcongestionflag=1;
  else
    mainlinkcongestionflag=0;
#ifdef SHOWLINKUTILIZATION
  cout<<">>bw ";
  for (c1=TREE_DEPTH-1;c1>=0;c1--) {
    cout<<"l"<<c1<<" "<<
      (leveltraffic[c1]/linksatlevel[c1])*100/table.getlinkcapacity(c1)<<"% ";
  }
  cout<<endl;
#endif
}


/* draws a point to know the % of the simulation */
void drawpoints(long max) {
  static long actual=0;
  static int cadapunt=0;
  static int npunts=0;

  actual++;
  cadapunt++;
  if (actual>=max) {
    cerr<<". - 100%"<<endl;
    actual=0;
    cadapunt=0;
    npunts=0;
  }
  else if (cadapunt==10) { // point calls each point
    cerr<<"."<<flush;
    cadapunt=0;
    npunts++;
    if (npunts==50) { // 50 points each line
      cerr<<" - "<<actual*100/max<<"%"<<endl;
      npunts=0;
    }
  }
}

/* returns the next slot after a batchtime */
int nextbatchslot(int numslot,double time,int video) {
#ifdef DYNAMICBATCH
  static double reqk=(double)(REQPERDAY)/86400*TIMESLOT;
  static double previousload=0;
  static int previousslot=0;
  double reqinslot;
  int parts,numpart,*sources,c1;
  int highercache=0;
  double finalclients,load,cumprobprev=0,cumprobcurr=0;
  int result;
#endif

#ifdef DYNAMICBATCH
  parts=videorep.getnumparts(video);
  for (numpart=0;numpart<parts;numpart++) {
    sources=videorep.getlocation(video,numpart);
    if (highercache<tree.getnodedepth(sources[0]))
      highercache=tree.getnodedepth(sources[0]);
    free(sources);
  }
  finalclients=pow(CHILDREN_EACH_NODE,highercache)*USERS_EACH_NODE;
  reqinslot=probreqlevel[highercache]*reqk*finalclients*
    timemodifier(time+BATCHFUTUREREF); // rho

  for (c1=1;c1<=(int)previousload;c1++)
    cumprobprev+=videorep.getprobofrequest(c1);

  load=previousload+(reqinslot*0.77)*(1-cumprobprev);

  for (c1=1;c1<=(int)load;c1++)
    cumprobcurr+=videorep.getprobofrequest(c1);

  if (load>MAXOUTPUT) {
    if (previousslot!=numslot) {
#ifdef SHOWSTATLOAD
      cout<<">>load "<<numslot<<" "<<load<<endl;
#endif
      previousload=load-MAXOUTPUT;
      previousslot=numslot;
    }
    return numslot+(int)((load-MAXOUTPUT)/(MAXOUTPUT*cumprobcurr));
  }
  else {
    if (previousslot!=numslot) {
#ifdef SHOWSTATLOAD
      cout<<">>load "<<numslot<<" "<<load<<endl;
#endif
      previousload=0;
      previousslot=numslot;
    }
    return numslot;
  }
#elif defined (STATICBATCH)
  return numslot+BATCHTIME;
#elif defined (CONGESTIONBATCH)
  if (mainlinkcongestionflag)
    return numslot+BATCHTIME;
  else
    return numslot;
#else
#error No batching policy defined, try STATICBATCH with BATCHTIME 0
#endif
}

#ifdef DYNAMICBATCH
/* calculates MAXOUTPUT */
double getmaxoutput(){
  int c1;
  long maxparts=0;
  double maxoutput=0;

  for (c1=1;c1<TREE_DEPTH;c1++) {
    //maxparts+=storedparts[c1];
    //maxoutput+=LINK_BW*pow(CHILDREN_EACH_NODE,TREE_DEPTH-c1)*storedparts[c1];
    maxoutput+=LINK_BW*pow(CHILDREN_EACH_NODE,TREE_DEPTH-c1)*probreqlevel[c1];
  }
  //maxoutput/=maxparts;
  if (maxoutput>LINK_BW*pow(CHILDREN_EACH_NODE,TREE_DEPTH-1)) {
    cerr<<"error calculating MAXOUTPUT"<<endl;
    exit(-2);
  }
  maxoutput/=(MEANVIDEODURATION*MEANVIDEOBW);
  return maxoutput;
}
#endif

/* sets probreqlevel and MAXOUTPUT */
void setglobalvars() {
  int video,continuar,parts,numpart,*sources,highercache;
  double popularity,cummpop;

  video=1;
  popularity=0;
  cummpop=0;
  highercache=0;
  continuar=1;
  while (continuar) {
    parts=videorep.getnumparts(video);
    for (numpart=0;numpart<parts;numpart++) {
      sources=videorep.getlocation(video,numpart);
      if (highercache<tree.getnodedepth(sources[0])) {
	probreqlevel[highercache]=popularity;
	highercache++;
	while (highercache<tree.getnodedepth(sources[0])) {
	  probreqlevel[highercache]=0;
	  highercache++;
	}
	//highercache=tree.getnodedepth(sources[0]);
	free(sources);
	cummpop+=popularity;
	popularity=0;
	if (highercache>=(TREE_DEPTH-1)) {
	  probreqlevel[highercache]=1-cummpop;
	  continuar=0;
	}
      }
    }
    popularity+=videorep.getprobofrequest(video);
    video++;
    if (video>NUMVIDEOS)
      continuar=0;
  }
#ifdef DYNAMICBATCH
  MAXOUTPUT=getmaxoutput();
#endif
}
