/*
   File:        diar.c
   Author:      Andrew W. Moore
   Created:     Mon Sep 21 16:26:05 EDT 1992
   Description: Diary to record partitions and worst-histories

   Copyright (C) 1992, Andrew W. Moore
*/

#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include "ambs.h"      /* Very basic operations */
#include "amma.h"      /* Fast, non-fragmenting, memory management */
#include "amar.h"      /* Obvious operations on 1-d arrays */
#include "amgr.h"      /* Basic (0,512)x(0,512) Graphics window */
#include "maxdim.h"    /* The MAX_DIM declaration */
#include "gpro.h"      /* Graphics projections kd->2d space */
#include "hype.h"      /* Hyper-rectangles from ../kdtr */
#include "region.h"    /* K-d region Data Structure */
#include "wrld.h"      /* Spec. of the World Control problem */
#include "whis.h"      /* History of worsts */
#include "part.h"      /* PARTitions and TESSalations */
#include "diar.h"      /* The diary data-structure */

void empty_diary(di)
diary *di;
{
  empty_worst_hist(&di->worst_hist);
  di -> first_part = NULL;
  di -> next_part = NULL;
  di -> third_part = NULL;
}

void init_diary(di)
diary *di;
{
  init_worst_hist(&di->worst_hist);
  empty_diary(di);
}


void add_to_diary(wld,ts,di,wst)
world *wld;
tess *ts;
diary *di;
worst *wst;
{
  part *part_now = part_search(ts,wst->mode,wst->nstate);

  if ( Verbosity > 50.0 )
    fprintf_worst(stdout,"worst-to-be-added",wst,wld,"\n");

  add_to_worst_hist(&di->worst_hist,wst,state_dim(wld,wst->mode));

  if ( Verbosity > 50.0 )
    fprint_worst_hist(stdout,"whist-with-new-addition",&di->worst_hist,wld);

  if ( di->first_part == NULL )
    di -> first_part = part_now;
  else if ( di -> next_part == NULL )
  {
    if ( !EQUAL_PARTS(part_now,di->first_part) )
      di -> next_part = part_now;
  }
  else if ( di -> third_part == NULL )
  {
    if ( !EQUAL_PARTS(part_now,di->next_part) )
      di -> third_part = part_now;
  }
  else
  {
    if ( !EQUAL_PARTS(part_now,di->third_part) )
      my_error("Add a fourth part to diary? No way dude!");
  }
}

bool is_diary_transition(wld,ts,di,psource,pdest)
world *wld;
tess *ts;
diary *di;
part **psource,**pdest;
{
  bool result;
  bool use_diary = FALSE;
  bool check_if_stuck = FALSE;
  *psource = NULL;
  *pdest = NULL;

  if ( di->first_part == NULL )
    use_diary = FALSE;
  else if ( di->next_part == NULL )
    check_if_stuck = TRUE;
  else if ( di->third_part != NULL )
    use_diary = TRUE;
  else if ( di->worst_hist.recent == NULL )
    my_error("is_diary_transition(): empty hist but first_part full");
  else
  {
    worst *wst = di->worst_hist.recent->worst;
    float middle[MAX_DIM];
    int i;
    int dim = state_dim(wld,wst->mode);

    for ( i = 0 ; i < dim ; i++ )
      middle[i] = middle_pivot(ts,di->next_part,i);

    if ( is_safely_in_part(di->next_part,middle,dim,wst->nstate) )
      use_diary = TRUE;
    else
      check_if_stuck = TRUE;
  }

  if ( use_diary && check_if_stuck )
    my_error("is_di_tr(). No!");
  else if ( use_diary )
  {
    result = TRUE;
    *psource = di -> first_part;
    *pdest = (di->third_part == NULL) ?
             di -> next_part :
             di -> third_part;
  }
  else if ( check_if_stuck && whist_is_stuck(wld,&di->worst_hist) )
  {
    result = TRUE;
    *psource = di -> first_part;
    *pdest = (di->next_part == NULL) ? di -> first_part : di -> next_part;
  }
  else
    result = FALSE;

  return(result);
}

/* STATE_SEQUENCE operations */

void init_state_sequence(ssq)
state_sequence *ssq;
{
  ssq->start = NULL;
  ssq->end = NULL;
}

seq_node *make_seq_node(nst,lgoal)
float *nst;
worst *lgoal;
/*
   Does *NOT* copy the memory of nst and lgoal... uses the memory given
*/
{
  seq_node *result = AM_MALLOC(seq_node);
  result -> nstate = nst;
  result -> local_goal = lgoal;
  result -> next = NULL;
  return(result);
}
  
void add_to_state_sequence(ssq,nst,lgoal)
state_sequence *ssq;
float *nst;
worst *lgoal;
/*
   Does *NOT* copy the memory of nst and lgoal... uses the memory given
*/
{
  seq_node *new = make_seq_node(nst,lgoal);
  if ( ssq->start == NULL )
  {
    ssq->start = new;
    ssq->end = new;
  }
  else
  {
    ssq->end->next = new;
    ssq->end = new;
  }
}

void draw_seq_node(gp,sn,run_mode)
gproj *gp;
seq_node *sn;
int run_mode;
{
  if ( sn -> nstate != NULL )
  {
    if ( sn->local_goal->mode == run_mode )
      gp_draw_floats_little_square(gp,sn->local_goal->nstate);
    gp_draw_floats_dot(gp,sn->nstate);
  }
}

void draw_seq_nodes(gp,sn,run_mode)
gproj *gp;
seq_node *sn;
int run_mode;
{
  for ( ; sn != NULL ; sn = sn -> next )
    draw_seq_node(gp,sn,run_mode);
}

void draw_state_sequence(gp,ssq,run_mode)
gproj *gp;
state_sequence *ssq;
int run_mode;
{
  draw_seq_nodes(gp,ssq->start,run_mode);
}

void print_seq_node(wld,sn,run_mode)
world *wld;
seq_node *sn;
int run_mode;
{
  printf_floats("nstate ",sn->nstate,state_dim(wld,run_mode)," ");
  fprintf_worst(stdout,"lgoal ",sn->local_goal,wld,"\n");
}

void print_seq_nodes(wld,sn,run_mode)
world *wld;
seq_node *sn;
int run_mode;
{
  int i = 0;
  for ( ; sn != NULL ; sn = sn -> next )
  {
    print_seq_node(wld,sn,run_mode);
    i++;
    if ( i % 20 == 0 ) wait_for_key();
  }
}

float *make_copy_floats(farr,size)
float *farr;
int size;
{
  float *result = AM_MALLOC_ARRAY(float,size);
  copy_floats(farr,result,size);
  return(result);
}

void augment_state_sequence_from_history(wld,whist,run_mode,lgoal,ssq)
world *wld;
worst_hist *whist;
int run_mode;
worst *lgoal;
state_sequence *ssq;
{
  int dim = state_dim(wld,run_mode);
  worst *local_goal_copy = NULL;
  worst_list *wl;
  for ( wl = whist->start ; wl != NULL ; wl = wl->after )
  {
    if ( wl -> worst -> mode == run_mode )
    {
      float *nstate = make_copy_floats(wl->worst->nstate,dim);      
      if ( local_goal_copy == NULL )
      {
        int goal_dim = state_dim(wld,lgoal->mode);
        local_goal_copy = create_worst(lgoal->mode,goal_dim);
        copy_worst(lgoal,local_goal_copy,goal_dim);
      }
      if ( Verbosity > 23.0 )
      {
        fprintf_worst(stdout,"Adding ",wl->worst,wld," lgoal ");
        fprintf_worst(stdout,"",lgoal,wld," to state sequence\n");
      }
      add_to_state_sequence(ssq,nstate,local_goal_copy);
    }
    else if ( ssq->end == NULL || ssq->end->nstate != NULL )
    {
      if ( Verbosity > 23.0 )
        printf("Adding NULL NULL to state sequence\n");
      add_to_state_sequence(ssq,(float *) NULL,(worst *) NULL);
    }
  }
}

bool seq_node_in(hy,sn)
hype *hy;
seq_node *sn;
{
  return( sn != NULL &&
          sn -> nstate != NULL &&
          is_inside_hype(hy,sn->nstate)
        );
}

/* HOW TRAJECTORIES ARE STORED IN PARTS.
   
   Each part has a "char *experience_data" field (see part.h). The
   part.c program, doesn't do anything with it, and isn't allowed to
   access it (except to NULL it when initializing a part).

   Now, we are going to use the experience_data data to store trajes.

   See diar.h for the trajes data structure.

   When all part are valid, the trajes of a part consist of a list
   of all the experiences (seq_nodes) which have just entered the part.
   Thus, for any seq_node in environment -> state_sequence, IF-AND-ONLY-IF
   seq_node is the member of a part's trajes, then either the
   seq_node has no predecessor in state_sequence, or else seq_node's
   predecessor was inside a different part, or else seq_node's
   predecessor was a "sequence jump (denoted by the predecessor's
   nstate being NULL).

   trajes are updated in two ways. After a partition transition, the
   new set of seq_nodes added to env->state_sequence are scanned, and
   appropriate seq_nodes added as trajes of appropriate parts.
     [THIS FIRST WAY IMPLEMENTED BY add_experience_data()]
    
   When a part is split, someone must, before the splitting, gather up
   the trajes of all the parts to be split. They are all appended together.
   Then, after splitting, all seq_nodes within all member of trajes are
   scanned, and those who should now become new trajes become so.
     [THIS SECOND WAY IMPLEMENTED BY insert_trajes()]
*/

trajes *add_to_trajes(sn,tjs)
seq_node *sn;
trajes *tjs;
{
  trajes *new = AM_MALLOC(trajes);
  new -> seq_node = sn;
  new -> next = tjs;
  return(new);
}

trajes *add_experience_data(ts,wld,run_mode,old_seq_node,old_seq_node_included)
tess *ts;
world *wld;
int run_mode;
seq_node *old_seq_node;
bool old_seq_node_included;
/*
   PRE: The part's experience data is valid up to old_seq_node.
          if old_seq_node_included also valid including old_seq_node, 
                                   but not beyond
          if old_seq_node_included == FALSE old_seq_node not valid and, 
                                   nor is beyond

   POST: It's valid also to the very end of what old_seq_node points to.
         *Also* Returns a list of new traj nodes. Note that the nodes
         of this list are different memory from anything connected to the
         parts, so once you've used this list of traje nodes, you should free
         it. Note also that one of these traj nodes may not be officially
         included among the parts. This is associated with the "tail";
         from the previos part we were in.
*/
{
  seq_node *sn = old_seq_node;
  hype *hy = create_hype(state_dim(wld,run_mode));
  trajes *result = NULL;

  if ( old_seq_node_included )/* Then find when things change in future */
  {
    if ( sn == NULL ) my_error("uoicv bni");
    if ( sn -> nstate != NULL ) /* We find current part & scan for a change */
    {
      part *pt = part_search(ts,run_mode,sn->nstate);
      hype_of_part(pt,hy);
      if ( seq_node_in(hy,sn) )
      {
        sn = sn -> next;
        if ( seq_node_in(hy,sn) )
        {
          result = add_to_trajes(sn,result); /* Traj associated with "tail"*/
          if ( Verbosity > 35.0 )
          {
            printf("Creating temp. 'tail' traj. First seq_node in it is:\n");
            print_seq_node(wld,sn,run_mode);
            fprintf_part(stdout,"tail-part-added-to",pt,ts);
          }

          sn = sn -> next;
          while ( seq_node_in(hy,sn) )
            sn = sn -> next;
        }
      }
    }
  }

  while ( sn != NULL )
  {
    while ( sn != NULL && sn -> nstate == NULL )
      sn = sn -> next;
    if ( sn != NULL )
    {
      part *pt = part_search(ts,run_mode,sn->nstate);
      add_to_part_trajes(pt,sn);
      result = add_to_trajes(sn,result);
      if ( Verbosity > 35.0 )
      {
        printf("Adding a new traj. The first seq_node in it is:\n");
        print_seq_node(wld,sn,run_mode);
        fprintf_part(stdout,"part-added-to",pt,ts);
      }

      hype_of_part(pt,hy);
    
      while ( seq_node_in(hy,sn) )
        sn = sn -> next;
    }
  }

  free_hype(hy);
  return(result);
}

bool is_seq_node_in_trajes(sn,tjs)
seq_node *sn;
trajes *tjs;
{
  bool result = FALSE;
  for ( ; !result && tjs != NULL ; tjs = tjs -> next )
    result = EQ_PTR(sn,tjs->seq_node);
  return(result);
}

trajes *append_trajes(t1,t2)
trajes *t1,*t2;
/*
   No copying or memory allocated. Just good clean pointer manipulation
   of the end of t1 (unless t1 is NULL of course)
*/
{
  trajes *result;
  if ( t1 == NULL )
    result = t2;
  else
  {
    result = t1;
    while ( t1 -> next != NULL )
      t1 = t1 -> next;
    t1 -> next = t2;
  }
  return(result);
}

void free_all_traj_nodes(tjs)
trajes *tjs;
{
  while ( tjs != NULL )
  {
    trajes *next = tjs -> next;
    am_free((char *)tjs,sizeof(trajes));
    tjs = next;
  }
}

trajes *part_trajes(pt)
part *pt;
{
  trajes *tjs = (trajes *) pt -> experience_data;
  return(tjs);
}

void add_to_part_trajes(pt,sn)
part *pt;
seq_node *sn;
{
  trajes *new = add_to_trajes(sn,part_trajes(pt));
  pt -> experience_data = (char *) new;
}

void insert_seq_node(ts,wld,run_mode,sn)
tess *ts;
world *wld;
int run_mode;
seq_node *sn;
/*
   Okay, we want to find the part(s) which
    (A) This sequence passes through
    (B) Don't know about it
   We follow sn, and find the part containing each member. Whenever
   sn should be included as a traj of the part it is. As soon as a
   member of sn is discovered which should be and is, then we
   stop.

   PRE:  The first member of sn is a valid start of a traj.
*/
{
  if ( sn == NULL )
    my_error("diar.c, insert_seq_node(): sn == NULL\n");
  else if ( sn -> nstate == NULL )
    my_error("diar.c, insert_seq_node(): sn -> nstate == NULL\n");
  else
  {
    part *pt = part_search(ts,run_mode,sn->nstate);
    hype *hy = create_hype(state_dim(wld,run_mode));
    while ( sn != NULL && !is_seq_node_in_trajes(sn,part_trajes(pt)) )
    {
      add_to_part_trajes(pt,sn);
      hype_of_part(pt,hy);
      while ( seq_node_in(hy,sn) )
        sn = sn -> next;
      while ( sn != NULL && sn -> nstate == NULL )
        sn = sn -> next;
      if ( sn != NULL )
        pt = part_search(ts,run_mode,sn->nstate);
    }
    free_hype(hy);
  }
}

void insert_trajes(ts,wld,run_mode,tjs)
tess *ts;
world *wld;
int run_mode;
trajes *tjs;
{
  for ( ; tjs != NULL ; tjs = tjs -> next )
    insert_seq_node(ts,wld,run_mode,tjs->seq_node);
}

void draw_trajes(gp,wld,pt)
gproj *gp;
world *wld;
part *pt;
{
  int run_mode = pt -> tess_id;
  hype *hy = create_hype(state_dim(wld,run_mode));
  trajes *tjs;
  seq_node *sn;

  hype_of_part(pt,hy);

  for ( tjs = part_trajes(pt) ; tjs != NULL ; tjs = tjs -> next )
  {
    for ( sn = tjs -> seq_node ; seq_node_in(hy,sn) ; sn = sn -> next )
      draw_seq_node(gp,sn,run_mode);
    if ( sn != NULL )
      draw_seq_node(gp,sn,run_mode);
  }
  free_hype(hy);
}

void print_trajes(wld,pt)
world *wld;
part *pt;
{
  int run_mode = pt -> tess_id;
  hype *hy = create_hype(state_dim(wld,run_mode));
  trajes *tjs;
  seq_node *sn;
  int i = 1;
  int line = 0;

  hype_of_part(pt,hy);

  for ( tjs = part_trajes(pt) ; tjs != NULL ; tjs = tjs -> next )
  {
    printf("Traj %d:\n",i);
    i += 1;

    for ( sn = tjs -> seq_node ; seq_node_in(hy,sn) ; sn = sn -> next )
    {
      print_seq_node(wld,sn,run_mode);
      line++;
      if ( line % 20 == 0 )
        wait_for_key();
    }
  }
  free_hype(hy);
}

/* HISTOS (HISTORICAL OUTCOMES) OPERATIONS */

histos *add_to_histos(histos_type,local_goal,nstate,hs)
int histos_type;
worst *local_goal;
float *nstate;
histos *hs;
/*
   Nothing is copied. Only memory for list node itself is allocated
*/
{
  histos *new = AM_MALLOC(histos);

  new -> histos_type = histos_type;
  new -> local_goal = local_goal;
  new -> depart_nstate = nstate;
  new -> next = hs;

  return(new);
}

void add_to_running_worst_hist(wh,nstate,run_mode,dim)
worst_hist *wh;
float *nstate;
int run_mode,dim;
{
  worst wst;
  wst.mode = run_mode;
  copy_floats(nstate,wst.nstate,dim);
  add_to_worst_hist(wh,&wst,dim);
}

histos *histos_from_trajes_node(ts,wld,run_mode,pt,tj,reap_eps)
tess *ts;
world *wld;
int run_mode;
part *pt;
trajes *tj;
float reap_eps;
{
  worst_hist wh;
  bool outside = FALSE;
  int dim = state_dim(wld,run_mode);
  seq_node *sn = tj -> seq_node;
  hype *hy = create_hype(dim);
  histos *result = NULL;

  if ( Verbosity > 22.0 )
    printf("Entered histos_from_part_node()\n");

  hype_of_part(pt,hy);
  init_worst_hist(&wh);

  while ( sn != NULL && !outside )
  {
    while ( sn != NULL && sn -> nstate == NULL ) sn = sn -> next;

    if ( sn != NULL )
    {
      outside = !is_inside_hype(hy,sn->nstate);
      if ( Verbosity > 31.0 )
        printf_floats("starting sn->nstate = ",sn->nstate,dim,
                      (outside) ? "is OUTSide\n" : "is_inside\n"
                     );

      if ( !outside )
      {
        worst *lgoal = sn -> local_goal;
        bool ok = TRUE;
        add_to_running_worst_hist(&wh,sn->nstate,run_mode,dim);

        while ( ok )
        {
          sn = sn -> next;
          ok = sn != NULL && sn -> nstate != NULL;

          if ( Verbosity > 32.0 )
          {
            if ( ok )
              printf_floats("sn->nstate = ",sn->nstate,dim,
                            !is_inside_hype(hy,sn->nstate) ? 
                            "is OUTSide\n" : "is_inside\n"
                           );
            else
              printf("No longer ok\n");
          }

          if ( ok )
          {
            ok = sn->local_goal->mode == lgoal->mode &&
                 is_scaled_worst_dist_below(wld,lgoal,sn->local_goal,reap_eps);
            if ( Verbosity > 31.0 )
              printf("histo_from_traj_node. Local goal changed\n");
          }

          if ( ok && !is_inside_hype(hy,sn->nstate) )
          {
            result = add_to_histos(HISTOS_DEPART,lgoal,sn->nstate,result);
            outside = TRUE;
            ok = FALSE;
          }

          if ( ok )
            add_to_running_worst_hist(&wh,sn->nstate,run_mode,dim);

          if ( ok && whist_is_stuck(wld,&wh) )
          {
            if ( Verbosity > 19.0 )
              printf("whist was stuck\n");
            result = add_to_histos(HISTOS_STUCK,lgoal,(float *)NULL,result);
            ok = FALSE;
          }

        }   
        empty_worst_hist(&wh);
      }
    }
  }
  free_hype(hy);
  return(result);
}

histos *append_histos(h1,h2)
histos *h1,*h2;
/*
   No copying or memory allocated. Just good clean pointer manipulation
   of the end of h1 (unless h1 is NULL of course)
*/
{
  histos *result;
  if ( h1 == NULL )
    result = h2;
  else
  {
    result = h1;
    while ( h1 -> next != NULL )
      h1 = h1 -> next;
    h1 -> next = h2;
  }
  return(result);
}

void append_histos_to_part(pt,hs)
part *pt;
histos *hs;
{
  histos *part_hs = (histos *) pt -> histos_data;
  histos *new_part_hs = append_histos(hs,part_hs);
  pt -> histos_data = (char *) new_part_hs;
}

void print_histos(wld,run_mode,hs)
world *wld;
int run_mode;
histos *hs;
{
  for ( ; hs != NULL ; hs = hs -> next )
  {
    fprintf_worst(stdout,"When I aimed for ",hs->local_goal,wld,"");
    if ( hs -> histos_type == HISTOS_STUCK )
      printf(" I got STUCK\n");
    else
      printf_floats("I ended at ",
                    hs->depart_nstate,state_dim(wld,run_mode),"\n"
                   );
  }
}

void draw_histos(gp,wld,run_mode,hs)
gproj *gp;
world *wld;
int run_mode;
histos *hs;
{
  for ( ; hs != NULL ; hs = hs -> next )
  {
    if ( hs->local_goal->mode == run_mode )
    {
      gp_draw_floats_little_square(gp,hs->local_goal->nstate);
      if ( hs -> histos_type != HISTOS_STUCK )
      {
        gp_draw_floats_dot(gp,hs->depart_nstate);
        gp_draw_floats_line(gp,hs->depart_nstate,hs->local_goal->nstate);
      }
    }
  }
}

void free_all_histos(hs)
histos *hs;
/*
   Frees only the nodes themselves
*/
{
  while ( hs != NULL )
  {
    histos *next = hs -> next;
    am_free((char *)hs,sizeof(histos));
    hs = next;
  }
}

void free_histos_of_part(pt)
part *pt;
{
  free_all_histos((histos *) (pt->histos_data));
}

void histos_from_trajes(ts,wld,run_mode,tjs,reap_eps)
tess *ts;
world *wld;
int run_mode;
trajes *tjs;
float reap_eps;
/*
   PRE:  trajes need not be all from same part.
   POST: the corresponding histos are all added to the histos_data of the part
*/
{
  for ( ; tjs != NULL ; tjs = tjs -> next )
  {
    if ( tjs -> seq_node == NULL || tjs -> seq_node -> nstate == NULL )
      my_error("diar.c, histos_from_trajes(ts,wld,run_mode,tjs,reap_eps)");
    else
    {
      histos *hs;
      part *pt = part_search(ts,run_mode,tjs->seq_node->nstate);
      if ( Verbosity > 22.0 )
        fprintf_part(stdout,"finding-histos-of",pt,ts);
      hs = histos_from_trajes_node(ts,wld,run_mode,pt,tjs,reap_eps);
      append_histos_to_part(pt,hs);
    }
  }
}


int number_user_trials(ss)
state_sequence *ss;
/*
   Looks at the number of seq_node->nstate == NULL occurences
*/
{
  seq_node *sq;

  int result = 0;
  for ( sq = ss -> start ; sq != NULL ; sq = sq -> next )
    if ( sq -> nstate == NULL )
      result += 1;

  return(result);
}

int number_seq_nodes(sq)
seq_node *sq;
/*
   The number of nodes (including sq) to end of seq_node list
*/
{
  int result = 0;
  for ( ; sq != NULL ; sq = sq->next )
    result += 1;

  return(result);
}

seq_node *start_of_trial_number(ss,tnum)
state_sequence *ss;
int tnum;
/*
   tnum = 0 => 1st trial etc.
   returns the first state seq node in the tnum'th user trial
*/
{
  seq_node *sq;
  if ( tnum >= number_user_trials(ss) )
  {
    printf("* start_of_trial_number: warning illegal trial number. Using 0\n");
    tnum = 0;
  }

  for ( sq = ss -> start ; tnum > 0 && sq != NULL ; sq=sq->next )
    if ( sq->nstate == NULL ) tnum--;

  return(sq);
}

void init_profile(prof,name,ts,wld,ss,gp)
profile *prof;
char *name;
tess *ts;
world *wld;
state_sequence *ss;
gproj *gp;
{
  prof -> name = name;
  prof -> trials = 0;
  prof -> tess = ts;
  prof -> world = wld;
  prof -> state_sequence = ss;
  prof -> gproj = gp;
}

void add_to_profile(prof)
profile *prof;
{
  int tnum = prof -> trials;

  if ( tnum >= MAX_TRIALS ) my_error("poukjpoyaxs");

  prof -> trials += 1;
  prof -> steps_in_trial[tnum] = 
    number_seq_nodes(start_of_trial_number(prof->state_sequence,tnum));
  prof -> number_partitions[tnum] = number_partitions(prof->tess);
}

void fprintf_profile(s,prof)
FILE *s;
profile *prof;
{
  time_t time_value;
  char *time_string;
  int tnum;

  printf("Calling time\n");
  time_value = time((time_t *) NULL);
  printf("Calling ctime\n");
  time_string = ctime(&time_value);

  fprintf(s,"date %s\n",time_string);
  fprintf(s,"trials %d\n",prof->trials);

  for ( tnum = 0 ; tnum < prof->trials ; tnum++ )
    fprintf(s,"trial %d steps %d partitions %d\n",
            tnum+1,prof->steps_in_trial[tnum],prof->number_partitions[tnum]
           );
}

void draw_trial_number(prof,suffix,tnum)
profile *prof;
char *suffix;
int tnum;
{
  char fname[200];
  seq_node *sq = start_of_trial_number(prof -> state_sequence,tnum);
  worst wst;
  int run_mode = mode_number_called(prof->world,"running");
  int dim = state_dim(prof->world,run_mode);
  wst.mode = run_mode;

  sprintf(fname,"%s-%s",prof->name,suffix);
  ag_on(fname);

  if ( prof->world->draw_structure != NULL )
    prof->world->draw_structure(prof->gproj,prof->world,prof->world->goal);

  gp_draw_geom_data(prof->gproj,prof->tess,run_mode);

  for ( ; sq != NULL && sq -> nstate != NULL ; sq = sq -> next )
  {
    copy_floats(sq->nstate,wst.nstate,dim);
    world_draw_worst(prof->gproj,prof->world,&wst);
  }
  ag_off();
  printf("Saved drawing of trajectory %d in file %s\n",tnum,fname);
}

void draw_on_clicked_box(mess)
char *mess;
{
  float x,y;
  float x1,y1,x2,y2;

  printf("Click on where you want the message '%s' to appear\n",mess);
  (void) ag_get_xy(&x,&y);

  x1 = x - 10.0 * strlen(mess);
  x2 = x + 10.0 * strlen(mess);

  y1 = y - 20.0;
  y2 = y + 20.0;

  ag_pen(1.0);
  ag_box(x1,y1,x2,y2);
  ag_pen(0.0);
  ag_boxed_print(x1,y1,x2,y2,mess);
  ag_rectangle(x1,y1,x2,y2);
}

void draw_problem(prof,start)
profile *prof;
worst *start;
{
  char fname[200];

  sprintf(fname,"%s-problem.ps",prof->name);
  ag_on(fname);

  if ( prof->world->draw_structure != NULL )
    prof->world->draw_structure(prof->gproj,prof->world,prof->world->goal);

  world_draw_worst(prof->gproj,prof->world,prof->world->goal);
  world_draw_worst(prof->gproj,prof->world,start);

  draw_on_clicked_box("Start");
  draw_on_clicked_box("Goal");
  ag_off();
  printf("Saved drawing of problem in file %s\n",fname);
}

void produce_profile(prof,start)
profile *prof;
worst *start;
{
  char fname[200];
  FILE *s;

  printf("Will perform a profile. Text in %s-profile.txt.\n",prof->name);

  sprintf(fname,"%s-profile.txt",prof->name);
  printf("Will open file\n");
  s = safe_fopen(fname,"w");

  printf("Will fprintf profile\n");
  fprintf_profile(stdout,prof);
  fprintf_profile(s,prof);
  fclose(s);

/*
  draw_trial_number(prof,"start.ps",0);
*/
  draw_problem(prof,start);
  if ( prof->trials > 0 )
    draw_trial_number(prof,"end.ps",prof->trials-1);
}

