/*
   File:        traversal.h
   Author:      Andrew W. Moore, Dan Pelleg
   Created:     Sun Apr  5 11:49:13 EDT 1998
   Description: Smart "blacklisting" traversal of kd-tree
   $Id: traversal.h,v 1.16 2004/01/16 01:33:19 dpelleg Exp $

   Copyright 1998, Carnegie Mellon University
*/


#ifndef TRAVERSAL_H
#define TRAVERSAL_H

#include "hrect.h"
#include "amiv.h"
#include "amdmex.h"
#include "simple_kdtree.h"

#define KDCACHE_MAXOWNERS (2)
#define KDCACHE_NO_OWNER (-1)

typedef struct {
  dyv_array *sums;				/* current sums for each center */
  ivec *nums;					/* current #pts owned for each center */
} means;

typedef struct {
  dyv *distortion;				/* in index i the distortion for center i */
  ivec *nums;					/* current #pts owned for center i */
} dist_info;

#ifdef UNUSED_NODESTATS
typedef struct {
	dist_info dist;				/* distortion measures */
	means means;
	ivec_array *membership;		/* in index i the points belonging to i */
	simple_kdtree **ctr_owner;	/* in index i a "big" node containing ctr i */
	void *stuff;				/* for future uses */
} nodestats;
#endif /* UNUSED_NODESTATS */

/* generic suff. stats about all centers */
typedef struct {
  dyv *distortion;				/* in index i the distortion for center i */
  ivec *nums;					/* current #pts owned for each center */
  dyv_array *sums;				/* current sums for each center */
  ivec_array *memb;             /* membership array: for each center, a sivec of points
                                   This may be NULL if no membership stats are needed.
                                */
} nodestats;

/* sufficient stats about a group of KD-nodes */
typedef struct cached_stats {
  int owners[KDCACHE_MAXOWNERS]; /* indices of owning centers; KDCACHE_NO_OWNER means none*/
  short nhits;					/* #times this node was hit */
  nodestats stat;				/* suff stats *just* in matching indices to those in owners */
} cached_stats;

/* structure for kdtree traversal generic procedure */
 
typedef struct traversal_stats {
  /* what to do when discovering a point belongs to a center */
  void (*update_point)(int point_idx, const dyv *point, int closest_ctr,
					   struct traversal_stats *stats);
  /* what to do when discovering a whole node belongs to a center */
  void (*update_bulk)(const simple_kdtree *node, int closest_ctr, 
					  struct traversal_stats *stats);
  /* can we prune this node, given it closest_ctr (sole owner)? */
  bool (*prune)(const simple_kdtree *node, int closest_ctr, 
				const struct traversal_stats *stats);

#if WANT_OTHER_REFINE_OWNERS
  /* MAKE and return an ivec of indexes of centers c that should
	 still be considered owners */
  ivec *(*mk_refine_owners)(hrect *hr, const ivec *considerables, vctr *centers);
#endif /* WANT_OTHER_REFINE_OWNERS */

  /* create a snapshot of current stats for use by... */
  cached_stats *(*mk_stat_snapshot)(const struct traversal_stats *stats, const ivec *candidates);

  /* given current stats and snapshot, compute "diff" for apply_cache to use*/
  cached_stats *(*mk_cache_entry)(const cached_stats *prestats, const struct traversal_stats *stats);
  
  /* given pre-computed caches suffstats, apply those to current stats */
  void (*apply_cache) (const cached_stats *cached, struct traversal_stats *stats); 

  /* free resources */
  void (*free_cached_stats) (cached_stats *cached);

  vctr *centers;				/* list of all centers */

  nodestats stat;				/* stores accumulating stats */

  FILE *aux;                    /* Where to print stats to (may not be assigned
                                 to anything meaningful) */
} traversal_stats;

/* the mk_*cached_stats allocate a cached_stats structure and initialize
   the "owners" array. BUT, they leave the stat field undefined 
   (so you have to assign to it yourself)
*/
cached_stats *mk_cached_stats(const ivec *owners);
cached_stats *mk_cp_cached_stats(const cached_stats *other);

/* free the owners, not the stat field */
void free_cached_stats(cached_stats *);

/* free all cached entries in tree */
void free_cache_entries(simple_kdtree *kd);

/* does the list of owners match the one in current_owners? */
bool cached_owners_match(cached_stats *, const ivec *current_owners);

nodestats kmeans_blacklist_mk_sums(kmeans_state *kmstate, const kmeans_parm *parm);

void kmeans_blacklist_aprx_find_sums(kmeans_state *kmstate, means *means);

/* Traverse kdtree.

   Hook to callbacks in appropriate places:
   stats.update_point when knows one point belongs to a center
   stats.update_bulk  when knows entire node belongs to a center
   stats.prune        to ask whether to prune this node or call recursively

   ivec *threats is sorted vector of centers to consider as owners of
        current node. ONLY CENTERS whose index is in threats are
		considered.

   If phased (in kmstate) != PHS_UNPHASED: then doing traversal on children
   of each parent, constrained to the region owne by the parent:

   A child center is allowed to own ONLY POINTS owned by its parent
   and within the sets of points closest to parent p the children of p compete
   for points in a closest-wins manner.

   If phased == PHS_UNPHASED, regular blacklisting traversal is happenning.
*/
void blacklist_traversal(kmeans_state *kmstate,
						 const ivec *candidates, traversal_stats *stats,
						 const kmeans_parm *parm);

/* the caching version of blacklist_traversal.
   Uses cached stats from previous runs if available.
   If not, calls blacklist_traversal.
   If conditions permit also stores results in cache.
*/
void cached_bl_traversal(kmeans_state *kmstate,
						 const ivec *candidates, traversal_stats *stats,
						 const kmeans_parm *parm);

/* MAKE and return ivec, list of indexes of centers c which could be "owners" 
   of all points in hrect in the sense each p in hrect has c has its closest
   center.
   ONLY those centers in considerables are considered in the first place.
   considerables assumed to be sorted.
   returned ivec is sorted.
*/
ivec *mk_refine_owners(const hrect *hr, const ivec *considerables, const vctr *centers);

/* sum of distortion for all centers */
double kmeans_distortion(simple_kdtree *kd, vctr *centers, const kmeans_parm *parm);

/* create and return the dist_info for the set of centers given data and kdtree kd */
dist_info mk_kmeans_distinfo(simple_kdtree *kd, vctr *centers, const kmeans_parm *parm);

/* print something that can be read by "kmeans membership"
   and converted into cluster membership */
void kmeans_printclusters(simple_kdtree *kd, vctr *centers,
                          const kmeans_parm *parm,
                          const char *out_filename);

/* "distortion" funcs for blacklist_traversal:
*/
void distortion_update_point(int point_idx, dyv *point, int closest_ctr,
							 traversal_stats *stats);
void distortion_update_bulk(simple_kdtree *node, int closest_ctr,
							traversal_stats *stats);

bool distortion_prune(const simple_kdtree *node, int closest_ctr,
					  const traversal_stats *stats);

void distortion_apply_cache(const cached_stats *cached, traversal_stats *stats);

void distortion_free_stats(dist_info *);

/* nodestats funcs -- unified means/distortion interface */

cached_stats *nodestats_mk_snapshot(const traversal_stats *stats, const ivec *candidates);

cached_stats *nodestats_mk_cache_entry(const cached_stats *prestats,
									   const traversal_stats *stats);

void nodestats_free_cache(cached_stats *cached);

nodestats nodestats_mk_stats(int ncenters, int ndims, bool with_memb);

void nodestats_update_point(int point_id, const dyv *point, int closest_ctr,
							traversal_stats *stats);
void nodestats_update_bulk(const simple_kdtree *node, int closest_ctr,
						   traversal_stats *stats);

void nodestats_apply_cache(const cached_stats *cached, traversal_stats *stats);

void nodestats_free_stats(nodestats *);

/* "print_clusterss" funcs for blacklist_traversal:
*/
void nodestats_print_point(int point_idx, const dyv *point, int closest_ctr,
                           traversal_stats *stats);
void nodestats_print_bulk(const simple_kdtree *node, int closest_ctr,
                          traversal_stats *stats);

#endif /* #ifndef TRAVERSAL_H */
