setst-sort.c   [plain text]


/*
 * Copyright (c) 2000-2001
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <regions.h>
#include <assert.h>
#include <stdio.h>
#include "bounds.h"
#include "setst-sort.h"


struct setst_union_
{
#ifdef NONSPEC
  sort_kind sort;
#endif
  int type;
  stamp st;
  gen_e_list exprs;
  gen_e_list proj_cache;
};

struct setst_inter_
{
#ifdef NONSPEC
  sort_kind sort;
#endif
  int type;
  stamp st;
  gen_e_list exprs;
};

struct setst_constant_
{
#ifdef NONSPEC
  sort_kind sort;
#endif
  int type;
  stamp st;
  char *name;
};

typedef struct setst_inter_ *setst_inter_;
typedef struct setst_union_ *setst_union_;
typedef struct setst_constant_ *setst_constant_;

static region tlb_cache_region;
static jcoll_dict tlb_dict;
static setst_var_list setst_vars;
static bool setst_changed = FALSE;

region setst_region;
term_hash setst_hash;
struct setst_stats setst_stats;

stamp setst_get_stamp(gen_e e)
{
#ifdef NONSPEC
  assert(e->sort == setst_sort);
#endif
  
  if ( ((setst_term)e)->type == VAR_TYPE)
    return st_get_stamp( (setst_var)e );
  
  else
    return ((setst_term)e)->st;
}

static bool eq(gen_e e1, gen_e e2)
{
  return ( setst_get_stamp(e1) == setst_get_stamp(e2) );
}

static gen_e_list get_union(gen_e e)
{
  assert ( ((setst_term)e)->type == UNION_TYPE);

  return ( (setst_union_) e)->exprs;
}

static gen_e_list get_inter(gen_e e)
{
  assert ( ((setst_term)e)->type == INTER_TYPE);

  return ( (setst_inter_) e)->exprs;
}

static void update_lower_bound(setst_var v, gen_e e)
{      
  if (setst_is_var(e))
    {
      if (st_add_lb(v,(setst_var)e))
	{
	  setst_stats.redundant_var++;
	}
      else
	{
	  setst_stats.added_var++;
	  setst_changed = TRUE;
	}
    }
  else
    {
      if (st_add_source(v, e,setst_get_stamp(e)))
	{
	  setst_stats.redundant_source++;
	}
      else
	{
	  setst_stats.added_source++;
	  setst_changed = TRUE;
	}
    }
  
}

static void update_upper_bound(setst_var v, gen_e e)
{
  assert(! setst_is_var(e));

  if (st_add_sink(v,e,setst_get_stamp(e)))
    {
      setst_stats.redundant_sink++;
    }
  else
    {
      setst_stats.added_sink++;
      setst_changed = TRUE;
    }
}


void setst_inclusion(con_match_fn_ptr con_match,gen_e e1, gen_e e2)
{
  if (eq(e1,e2))
    return;

  else if ( setst_is_zero(e1) || setst_is_one(e2) )
    return;

  else if (setst_is_union(e1))
    {
      gen_e_list_scanner scan;
      gen_e temp;

      gen_e_list exprs = get_union(e1);
      
      gen_e_list_scan(exprs,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  setst_inclusion(con_match,temp,e2);
	}

      return;
    }

  else if (setst_is_inter(e2))
    {
      gen_e_list_scanner scan;
      gen_e temp;
      
      gen_e_list exprs = get_inter(e2);

      gen_e_list_scan(exprs,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  setst_inclusion(con_match,e1,temp);
	}

      return;
    }

  else if (setst_is_var(e2))
    {
      setst_var v = (setst_var)e2;

      update_lower_bound(v,e1);
    }

  else if (setst_is_var(e1))
    {
      setst_var v = (setst_var)e1;

      update_upper_bound(v,e2);
    }

  else con_match(e1,e2);
}

#ifdef NONSPEC
static struct setst_term zero = {ZERO_TYPE,setst_sort,ZERO_TYPE};
static struct setst_term one  = {ONE_TYPE,setst_sort,ONE_TYPE};
#else
static struct setst_term zero = {ZERO_TYPE,ZERO_TYPE};
static struct setst_term one  = {ONE_TYPE,ONE_TYPE};
#endif /* NONSPEC */

gen_e setst_zero(void)
{
  return (gen_e)&zero;
}

gen_e setst_one(void)
{
  return (gen_e)&one;
}

gen_e setst_fresh(const char *name)
{
  setst_var v = st_fresh(setst_region,name);
  setst_var_list_cons(v,setst_vars);
  return (gen_e)v;
}

gen_e setst_fresh_large(const char *name)
{
  setst_var v = st_fresh_large(setst_region,name);
  setst_var_list_cons(v,setst_vars);
  return (gen_e)v;
}

gen_e setst_fresh_small(const char *name)
{
  setst_var v = st_fresh_small(setst_region,name);
  setst_var_list_cons(v,setst_vars);
  return (gen_e)v;
}

gen_e setst_constant(const char *str) deletes
{
  stamp st[2];
  gen_e result;
  char *name = rstrdup(setst_region,str);

  assert (str != NULL);
  
  st[0] = CONSTANT_TYPE;
  st[1] = stamp_string(name); 

  if ( (result = term_hash_find(setst_hash,st,2)) == NULL)
    {
      setst_constant_ c = ralloc(setst_region, struct setst_constant_);
      c->type = CONSTANT_TYPE;
      c->st = stamp_fresh();
      c->name = name;

      result = (gen_e) c;
      term_hash_insert(setst_hash,result,st,2);
      
      setst_stats.distinct_constants++;
      
      return result;
    }
  
  else
    {
      setst_stats.hashed_constants++;
      return result;
    }
}

static bool filter_zero(const gen_e e)
{
  return (!setst_is_zero(e));
}


static bool filter_one(const gen_e e)
{
  return (!setst_is_one(e));
}

gen_e setst_union(gen_e_list exprs) deletes
{
  gen_e_list filtered = gen_e_list_filter(setst_region,exprs,filter_zero);
  
  if ( gen_e_list_empty(filtered) )
    {
      setst_stats.filtered_unions++;
      return setst_zero();
    }
  else if (gen_e_list_length(filtered) == 1)
    {
      setst_stats.filtered_unions++;
      return gen_e_list_head(filtered);
    }

  else 
    {
      int i = 0;
      gen_e temp,result;
      gen_e_list_scanner scan;
      stamp st[ gen_e_list_length(filtered) + 1 ];
     
      st[0] = UNION_TYPE;

      gen_e_list_scan(filtered,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  st[++i] = setst_get_stamp(temp);
	}
      
      if ( (result = 
	   term_hash_find(setst_hash,st,gen_e_list_length(filtered)+1)) 
	   == NULL )
	{
	  struct setst_union_ *u = ralloc(setst_region,struct setst_union_);
	  
	  u->type = UNION_TYPE;
	  u->st = stamp_fresh();
	  u->proj_cache = new_gen_e_list(setst_region);
	  u->exprs = filtered;
	 
	  result = (gen_e)u;
	  term_hash_insert(setst_hash,result,st,gen_e_list_length(filtered)+1);
	
	  setst_stats.distinct_unions++;
	  return result;
	}
      else
	{
	  setst_stats.hashed_unions++;
	  return result;
	}
    }
}

gen_e setst_inter(gen_e_list exprs) deletes
{
  gen_e_list filtered = gen_e_list_filter(setst_region,exprs,filter_one);
  
  if ( gen_e_list_empty(filtered) )
    {
      setst_stats.filtered_intersections++;
      return setst_one();
    }
  else if (gen_e_list_length(filtered) == 1)
    {
      setst_stats.filtered_intersections++;
      return gen_e_list_head(filtered);
    }

  else 
    {
      int i = 0;
      gen_e temp,result;
      gen_e_list_scanner scan;
      stamp st[ gen_e_list_length(filtered) + 1 ];
     
      st[0] = INTER_TYPE;

      gen_e_list_scan(filtered,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  st[++i] = setst_get_stamp(temp);
	}
      
      if ( (result = 
	   term_hash_find(setst_hash,st,gen_e_list_length(filtered)+1)) 
	   == NULL )
	{
	  struct setst_inter_ *u = ralloc(setst_region,struct setst_inter_);
	  
	  u->type = UNION_TYPE;
	  u->st = stamp_fresh();
	  u->exprs = filtered;
	 
	  result = (gen_e)u;
	  term_hash_insert(setst_hash,result,st,gen_e_list_length(filtered)+1);
	  
	  setst_stats.distinct_intersections++;
	  
	  return result;
	}
      else 
	{
	  setst_stats.hashed_intersections++;
	  return result;
	}
    }
}


gen_e_list setst_get_union(gen_e e)
{
  assert (((setst_term)e)->type == UNION_TYPE);

  return ((setst_union_)e)->exprs;
}


gen_e_list setst_get_inter(gen_e e)
{
  assert (((setst_term)e)->type == INTER_TYPE);

  return ((setst_inter_)e)->exprs;
}

static void invalidate_tlb_cache(void)
{
  assert(tlb_cache_region);

  jcoll_delete_dict(tlb_dict);
  setst_var_list_app(setst_vars,st_clear_tlb_cache);
  deleteregion_ptr(&tlb_cache_region);
  
  tlb_cache_region = newregion();
  tlb_dict = jcoll_create_dict(tlb_cache_region,setst_get_stamp);
}

static void set_tlb_cache(setst_var v,jcoll j)
{
  st_set_tlb_cache(v,j);
}

static void collect_sinks(bounds b,setst_var v)
{
  gen_e sink;
  gen_e_list_scanner scan;

  gen_e_list_scan(st_get_sinks(v),&scan);

  while (gen_e_list_next(&scan,&sink))
    {
      bounds_add(b,sink,setst_get_stamp(sink));
    }  
}

static void collect_sources(bounds b, setst_var v)
{
  gen_e source;
  gen_e_list_scanner scan;

  gen_e_list_scan(st_get_sources(v),&scan);

  while (gen_e_list_next(&scan,&source))
    {
      bounds_add(b,source,setst_get_stamp(source));
    }  
}

static void collect_lower_bounds(bounds b, setst_var v)
{
  setst_var lb;
  setst_var_list_scanner scan;

  setst_var_list_scan(st_get_lbs(v),&scan);

  while (setst_var_list_next(&scan,&lb))
    {
      bounds_add(b,(gen_e)lb,st_get_stamp(lb));
    }  
}
 
static void apply_sources(setst_var witness, bounds sources)
{
  gen_e source;
  gen_e_list_scanner scan;

  gen_e_list_scan(bounds_exprs(sources),&scan);

  while (gen_e_list_next(&scan,&source))
    {
      if ( st_add_source(witness,source,setst_get_stamp(source)))
	setst_stats.redundant_source++;
	
      else
	setst_stats.added_source++;
    }  
}

static void apply_sinks(setst_var witness, bounds sinks)
{
  gen_e sink;
  gen_e_list_scanner scan;

  gen_e_list_scan(bounds_exprs(sinks),&scan);

  while (gen_e_list_next(&scan,&sink))
    {
      if (st_add_sink(witness,sink,setst_get_stamp(sink)))
	setst_stats.redundant_sink++;
	
      else
	setst_stats.added_sink++;
    }  
}

static void apply_lower_bounds(setst_var witness,bounds lower)
{
  gen_e lb;
  gen_e_list_scanner scan;

  gen_e_list_scan(bounds_exprs(lower),&scan);

  while (gen_e_list_next(&scan,&lb))
    {
      if (st_add_lb(witness,(setst_var)lb))
	setst_stats.redundant_var++;
      else
	setst_stats.added_var++;
    }  
}

static void collapse_cycle(setst_var witness, setst_var_list cycle) deletes
{
  setst_var_list_scanner var_scan;
  setst_var temp;
  region scratch_rgn = newregion();

  bounds sources = bounds_create(scratch_rgn);
  bounds sinks = bounds_create(scratch_rgn);
  bounds lower = bounds_create(scratch_rgn);
 

  setst_stats.cycles_collapsed++;

  /* force at least another iteration */
  setst_changed = TRUE;

  /* collect all bounds */
  setst_var_list_scan(cycle,&var_scan);
  while (setst_var_list_next(&var_scan,&temp))
    {
      collect_sources(sources,temp);
      collect_sinks(sinks,temp);
      collect_lower_bounds(lower,temp);  
    }
 
  /* unify all vars */
  st_unify(witness,cycle);

  /* add all bounds back */
  apply_sources(witness,sources);
  apply_sinks(witness,sinks);
  apply_lower_bounds(witness,lower);
 
  /* cleanup */
  bounds_delete(sources);
  bounds_delete(sinks);
  bounds_delete(lower);
  deleteregion(scratch_rgn);

  /* remove self edges */
  st_repair_bounds(witness);
}
/*
static bool cycle_detect(setst_var goal, setst_var_list path,
			 setst_var_list *result)
{
  int pos = st_get_path_pos(goal);
  setst_stats.cycles_searched++;
  
  if (pos)
    {  
      setst_var_list_scanner scan;
      setst_var temp;
      setst_var_list cycle = new_setst_var_list(tlb_cache_region);
      
      setst_var_list_scan(path,&scan);
      while(setst_var_list_next(&scan,&temp))
	{
	  if (st_get_path_pos(temp) >= pos)
	    setst_var_list_cons(temp,cycle);
	}
      
      *result = cycle;
      return TRUE;
    }
  
  else 
    return FALSE;
}

*/
static bool cycle_detect(setst_var goal, setst_var_list path,
			 setst_var_list *result)
{
  setst_var_list cycle = 
    setst_var_list_reverse(setst_var_list_copy(tlb_cache_region,path));

  setst_stats.cycles_searched++;

  while (!setst_var_list_empty(cycle) && 
	 !eq((gen_e)setst_var_list_head(cycle),(gen_e)goal))
    {
      setst_var_list_tail(cycle);
    }
  
  if (setst_var_list_empty(cycle))
    {
      return FALSE;
    }
  else
    {
      *result = cycle;
      return TRUE;
    }
}

static jcoll tlb_aux(gen_e e,int path_len,setst_var_list path) deletes
{
  if (setst_is_var(e))
    {
      setst_var_list cycle;
      setst_var v = (setst_var)e;
      if ( cycle_detect(v,path,&cycle) )
	{
	  setst_stats.cycles_length += setst_var_list_length(cycle);
	  collapse_cycle(v,cycle);
	  return NULL;
	}
      else
	{
	  if (st_get_tlb_cache(v) != NULL)
	    return st_get_tlb_cache(v);
	  else 
	    {
	      jcoll result;
	      setst_var_list_scanner scan;
	      setst_var lb;
	      jcoll_list jvars = new_jcoll_list(tlb_cache_region);
	      
	      gen_e_list sources = gen_e_list_copy(tlb_cache_region,
						   st_get_sources(v));
	      
	      st_set_path_pos(v,path_len);
	      setst_var_list_scan(st_get_lbs(v),&scan);
	      while (setst_var_list_next(&scan,&lb))
		{
		  setst_var_list_cons(v,path);
		  jcoll_list_cons(tlb_aux((gen_e)lb,++path_len,path),
				  jvars);
		  setst_var_list_tail(path); 
		}
	      
	      if (! gen_e_list_empty(sources))
		jcoll_list_cons(jcoll_create_chain(tlb_dict,sources),
				jvars);
	      result = jcoll_jjoin(tlb_dict,jvars);
	      set_tlb_cache(v,result);
	      st_set_path_pos(v,0);
	      return result;
	    }
	  
	}
    }
  else if (setst_is_union(e))
    {
      gen_e_list_scanner scan;
      gen_e temp;
      jcoll_list jexprs = new_jcoll_list(tlb_cache_region);
      
      gen_e_list_scan(setst_get_union(e),&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  jcoll_list_cons(tlb_aux(temp,++path_len,path),jexprs);
	}
      
      return jcoll_jjoin(tlb_dict,jexprs);
    }
  else
    {
      fail("Unmatched case in setst tlb computation\n");
      return NULL;
    }
}
static gen_e_list tlb(gen_e e)
{
  return jcoll_flatten(tlb_dict,
		       tlb_aux(e,1,new_setst_var_list(tlb_cache_region)) );
}
static void match_sinks(incl_fn_ptr setst_incl)
{
  gen_e_list_scanner tlb_scanner, sink_scanner;
  setst_var_list_scanner var_scanner;
  setst_var v;
  gen_e lb, sink;
  
  setst_var_list_scan(setst_vars,&var_scanner);
  
  while (setst_var_list_next(&var_scanner,&v))
    {
      gen_e_list tlbs = tlb((gen_e)v);
      gen_e_list snks = st_get_sinks(v);
      
      
      if(gen_e_list_empty(st_get_sinks(v)))
	{
	  setst_stats.no_sinks++;
	  continue;
	}
      else if(st_get_seen(v))
	{
	  setst_stats.incycle_vars++;
	  continue;
	}
      else if (gen_e_list_length(tlbs) == st_get_src_sz(v) 
	       && gen_e_list_length(snks) == st_get_snk_sz(v) )
	{
	  setst_stats.unchanged_vars++;
	  continue;
	}
      st_set_seen(v,TRUE);
      
      st_set_src_sz(v,gen_e_list_length(tlbs));
      st_set_snk_sz(v,gen_e_list_length(snks));
      
      gen_e_list_scan(tlbs,&tlb_scanner);
      
      while (gen_e_list_next(&tlb_scanner,&lb))
	{
	  gen_e_list_scan(snks,&sink_scanner);
	  
	  while (gen_e_list_next(&sink_scanner,&sink))
	    setst_incl(lb,sink);
	}
    }
}
static void iterate(incl_fn_ptr setst_incl)
{
  setst_var_list_scanner var_scanner;
  setst_var v; 
  /* static int iterations = 0; */
  setst_changed = FALSE;
  
  setst_var_list_scan(setst_vars,&var_scanner);
  while (setst_var_list_next(&var_scanner,&v))
    {
      st_set_seen(v,FALSE);
    }
  
  invalidate_tlb_cache();
  match_sinks(setst_incl);
  
  /*  fprintf(stderr,"Iterations : %d\n",++iterations); */
  
  if (setst_changed)
    iterate(setst_incl);
}  
gen_e_list setst_tlb(gen_e e,incl_fn_ptr setst_incl) deletes
{
  if (! setst_changed)
    {
      return tlb(e);
    }
  else
    {
      iterate(setst_incl);
      return tlb(e);
    }
  
}

void setst_set_proj_cache(gen_e e, gen_e elem)
{
  if (setst_is_union(e))
    {
      setst_union_ u = (setst_union_)e;
      gen_e_list_cons(elem,u->proj_cache);
    }
}

gen_e_list setst_get_proj_cache(gen_e e)
{

  if (setst_is_union(e))
    {
      setst_union_ u = (setst_union_)e;
      return u->proj_cache;
    }
  else
    {
      fail("Term does not cache projections\n");
      return NULL;
    }
}

void setst_init(void)
{
  setst_region = newregion();
  tlb_cache_region = newregion();
  setst_hash = make_term_hash(setst_region);
  setst_vars = new_setst_var_list(setst_region);
  tlb_dict = jcoll_create_dict(tlb_cache_region,setst_get_stamp);
}

void setst_reset(void) deletes
{
  term_hash_delete(setst_hash);
  deleteregion_ptr(&setst_region);

  setst_region = newregion();
  setst_hash = make_term_hash(setst_region);
  setst_vars = new_setst_var_list(setst_region);
  invalidate_tlb_cache();
  setst_changed = FALSE;
}

bool setst_is_zero(gen_e e)
{
  return ((setst_term)e)->type == ZERO_TYPE;
}

bool setst_is_one(gen_e e)
{  
  return ((setst_term)e)->type == ONE_TYPE;
}

bool setst_is_var(gen_e e)
{
  return ((setst_term)e)->type == VAR_TYPE;
}

bool setst_is_union(gen_e e)
{
  return ((setst_term)e)->type == UNION_TYPE;
}

bool setst_is_inter(gen_e e)
{
  return ((setst_term)e)->type == INTER_TYPE;
}

char *setst_get_constant_name(gen_e e)
{
  assert( ((setst_term)e)->type == CONSTANT_TYPE );
  
  return ((setst_constant_)e)->name;
}

void setst_print_stats(FILE *f)
{ 
  fprintf(f,"\n========== SetST Var Stats ==========\n");
  fprintf(f,"Fresh : %d\n",setst_stats.fresh); 
  fprintf(f,"Fresh Small : %d\n",setst_stats.fresh_small);
  fprintf(f,"Fresh Large : %d\n",setst_stats.fresh_large);
  fprintf(f,"Total : %d\n",setst_stats.fresh + setst_stats.fresh_small 
	  + setst_stats.fresh_large);
  fprintf(f,"\n========== SetST Sort Stats ==========\n");
  fprintf(f,"\n");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Additions");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Var:   %d\n",setst_stats.added_var);
  fprintf(f,"Source: %d\n",setst_stats.added_source);
  fprintf(f,"Sink:   %d",setst_stats.added_sink);
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Total:  %d",setst_stats.added_var + setst_stats.added_source
	  + setst_stats.added_sink);
  fprintf(f,"\n");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Redundant");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Var:   %d\n",setst_stats.redundant_var);
  fprintf(f,"Source: %d\n",setst_stats.redundant_source);
  fprintf(f,"Sink:   %d",setst_stats.redundant_sink);
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Total:  %d\n",
	 setst_stats.redundant_var + setst_stats.redundant_source
	  + setst_stats.redundant_sink);
 
  fprintf(f,"\n");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Iteration Optimizations");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Skipped vars:   %d\n",setst_stats.incycle_vars);
  fprintf(f,"Unchanged vars: %d\n",setst_stats.unchanged_vars);
  fprintf(f,"Vars w/o sinks: %d\n",setst_stats.no_sinks);
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Cycles");
  fprintf(f,"\n------------------------------\n");
  fprintf(f,"Collapsed:      %d\n",setst_stats.cycles_collapsed);
  fprintf(f,"Searched:       %d\n",setst_stats.cycles_searched);
  fprintf(f,"Hit rate:       %f\n",
	 ((float)setst_stats.cycles_collapsed)/((float)setst_stats.cycles_searched));
  fprintf(f,"Average Length: %f\n",
	 ((float)setst_stats.cycles_length) / ((float)setst_stats.cycles_collapsed));
  fprintf(f,"=====================================\n");
}