/*
   File:        dcube.h
   Author:      Andrew W. Moore
   Created:     Wed Dec 30 06:33:12 EST 1998
   Description: Header for CLI for fast ID3

   Copyright 2000, Schenley Park Research Inc
*/


#ifndef DCUBE_H
#define DCUBE_H

#include "amdmex.h"
#include "saarray.h"

/* 
   A dcube is a D-dimensional array in which each cell contains
   a dyv. All dyvs must be the same length. The implementation is
   as a tree where at the i'th dimension we split into arity[i]
   subtrees.

   If a dcube child has all zero entries everywhere it is represented
   as NULL 

   Typically you would use a dcube as the probability table in a bayesnet
   node.  For instance, suppose there is a node X with parents A, B, and C.
   Let node X correspond to an attribute with 3 possible values x1,x2,x3.  
   Let the parents have the following possible values:
   Parent A can have two possible values -- a1, a2
   Parent B can have two possible values -- b1, b2
   Parent C can have three possible values -- c1, c2, c3

   The dcube corresponding to the probability table in node X would have
   3 levels, one for each parent.  Suppose we let the levels be in the order
   of A, B, and C.  Pictorially, the dcube would look like:

                             __________ __________
                            |    a1    |    a2    |
                             ---------- ----------
                           /                         \
                          /                           \
		______ ______                     ______ ______
	       |  b1  |  b2  |                   | b1   |  b2  |
                ------ ------                     ------ ------
              /       |       \                 /       |       \
             /        |        \               /        |        \
     __ __ __     __ __ __     __ __ __    __ __ __    __ __ __    __ __ __
    |c1|c2|c3|   |c1|c2|c3|   |c1|c2|c3|  |c1|c2|c3|  |c1|c2|c3|  |c1|c2|c3| 
     -- -- --     -- -- --     -- -- --    -- -- --    -- -- --    -- -- --

   Each dcube node in the very last level contains a dyv of size 3.  In the 
   bayesnet context, the dyv corresponds to the probability of X taking on 
   values x1,x2,x3 given the values of its parents.  For example, if we
   follow the leftmost branch, the the dyv stored in the last dcube node would
   contain the following information:

   Pr( X = x1 | A = a1, B = b1, C = c1 )
   Pr( X = x2 | A = a1, B = b1, C = c1 )
   Pr( X = x3 | A = a1, B = b1, C = c1 )

*/

typedef struct dcube
{
  dyv *x;
  int size;
  struct dcube **children;
} dcube;



#ifdef AMFAST
#define dcube_is_leaf(dc) ((dc)->children==NULL)
#else
bool slow_dcube_is_leaf(dcube *dc);
#define dcube_is_leaf(dc) slow_dcube_is_leaf(dc)
#endif

#ifdef AMFAST
#define dcube_num_children(dc) ((dc)->size)
#else
int slow_dcube_num_children(dcube *dc);
#define dcube_num_children(dc) slow_dcube_num_children(dc)
#endif

#ifdef AMFAST
#define dcube_child_ref(dc,i) ((dc)->children[i])
#else
dcube *slow_dcube_child_ref(dcube *dc,int i);
#define dcube_child_ref(dc,i) slow_dcube_child_ref(dc,i)
#endif

/* Makes a zero-dimensional (thus a leaf) dcube containing one cell. 
   The contents of that one cell will be a copy of x */
dcube *mk_dcube_from_dyv(dyv *x);

/* Makes a non-leaf node. It's children should be filled in by
   directly assinging to the dc->children[i] array, where you
   can be sure that that array begins full of NULLs and has
   size == size.
*/
dcube *mk_internal_dcube(int size);

void free_dcube(dcube *dc);

void fprintf_dcube(FILE *s,char *m1,dcube *dc,char *m2);

void pdcube(dcube *dc);

/* This works properly even if dc==NULL */
dcube *mk_copy_dcube(dcube *dc);

/* dc := a * dc */
void dcube_scalar_mult(dcube *dc,double a);

dcube *mk_dcube_scalar_mult(dcube *dc,double a);

bool all_zeroes(dyv *x);

/* Subtracts y from *x. *x is changed, y is unchanged.
   PRE: at all levels, arities of x and y must match. 
        All fields in all results must be non-negative.

   Returns a CANONICAL dcube in that there will be no leaf nodes
   that have all x of zero and no internal nodes with all
   children NULL.

   If x and y are equal, frees *x and sets *x == NULL.
*/
void dcube_subtract(dcube **x,dcube *y);

/* Adds y to *x. *x is changed, y is unchanged.
   PRE: at all levels, arities of x and y must match. 
        All fields in all results must be non-negative.

   Returns a CANONICAL dcube in that there will be no leaf nodes
   that have all x of zero and no internal nodes with all
   children NULL.
*/
void dcube_add(dcube **x,dcube *y);

int dyv_array_first_dyv_size(dyv_array *da);

bool dcube_is_zero(dcube *dc);

bool dcube_contains_zero(dcube *dc);

/* values[i] are the values in the ith row of a compressed
   truth table represented by the dcube. x[i] is the
   aggregate_x matching the given values. */
void make_dcube_explain_info(dcube *dc,
                            int dcube_num_atts,
                            ivec_array **values,
                            dyv_array **x);

/* Returns number of non-zero cells in dcube */
int dcube_num_entries(dcube *dc);

dcube *mk_zero_leaf_dcube(int dyv_size);

int dcube_max_count(dcube *dc);

/* Treating values as a set of indexes into the K-dimensional
   array represented by dc, this function returns the sumvec
   x corresponding to the x stored in the specified array cell.

   If the values in x are all zero, returns NULL instead of a
   vector of zeroes. 

   PRE: values is exactly the same length as the number of dimensions
        of the dcube.
*/
dyv *dcube_values_to_x(dcube *dc,ivec *values);

/* As above but just returns the count of the sumvec (i.e.
   the first array element of x) */
int dcube_values_to_count(dcube *dc,ivec *values);

#ifdef AMFAST
#define check_dcube(dc)
#else
void slow_check_dcube(dcube *dc);
#define check_dcube(dc) slow_check_dcube(dc)
#endif

/* This function will my_error if any cell in dc contains a
   statvec summing to zero. */
void check_dcube_nonzero(dcube *dc);

/* Prints a dcube in very nice format to stdout.

   dim_to_name[i] is the name of the i'th dimension (i.e. the i'th tree 
   level) of the dcube.

   dim_to_valnames[i,j] is the name of the j'th value of the i'th dimension

   statvec_names[i] is the name of the i'th component of the statvec

   (remember a dyv stored in a cell of a dcube is called a statvec)
*/
void explain_dcube(dcube *dc,string_array *dim_to_name,
		   sa_array *dim_to_valnames,string_array *statvec_names);

string_array *mk_int_string_array(char *prefix,int size);

/* Returns -1 if dc has no leaf nodes */
int dcube_num_dims(dcube *dc);

/* Returns -1 if dc has no leaf nodes */
int dcube_statvec_size(dcube *dc);

/* Ivec of arities for the given dcube's dims */
ivec *mk_dcube_dim_to_arity(dcube *dc);

void explain_dcube_simple(dcube *dc);

int dcube_num_non_zero_statvec_entries(dcube *dc);

/* Scales statvec so that it has a sum of one. In other words,
   all components of sv are multiplied by 1/dyv_sum(sv).

   Prior to the linear transform to cause a sum to one, a
   constant (sumone_epsilon) is added to each element of the
   statvec */
void sumone_dyv(dyv *x,double sumone_epsilon);

/* Scales each statvec in dc individually so that each statvec
   a sum of one. In other word, if sv is a statvec in dc, then
   all components of sv are multiplied by 1/dyv_sum(sv).

   Prior to the linear transform to cause a sum to one, a
   constant (sumone_epsilon) is added to each element of the
   statvec */
void sumone_dcube_statvecs(dcube *dc,double sumone_epsilon);

/* Returns the sum of all the statvecs owned by dc.
   PRE: dc is non-NULL and dc contains at least one non-null statvec */
dyv *mk_dcube_sum_statvecs(dcube *dc);

/* NULL rows means use them all */
/* returns sum over r in "rows" of row_to_x[r] */ 
/* PRE: There must be at least one row in the sum */
dyv *mk_aggregate_x_from_row_to_x_subset(dyv_array *row_to_x,ivec *rows);

#endif /* #ifndef DCUBE_H */
