/*
   File:        shape.h
   Author:      Andrew W. Moore
   Created:     Sat Aug 14 12:50:02 EDT 1999
   Description: Header for SPR/Auton Shape of a datset Structure

   Copyright 1999, Schenley Park Research
*/


#ifndef SHAPE_H
#define SHAPE_H
#include "colshape.h"

/* #define MAX_ATTS 2000 */

/* A shape represents information about every attribute in a dataset. */
typedef struct shape
{
  int num_atts;  /* The number of attributes */
  int array_size;
  colshape **cs_array;  /* cs[i] gives info about the ith attribute.
                     where 0 <= i < num_atts */
} shape;

#ifdef AMFAST

#define shape_colshape_ref(sh,attnum) ((sh)->cs_array[attnum])
#define shape_check_coltype(sh,attnum,coltype) /* do nothing */

#else

colshape *slow_shape_colshape_ref(const shape *sh,int attnum);
#define shape_colshape_ref(sh,attnum) slow_shape_colshape_ref(sh,attnum)

void slow_shape_check_coltype(const shape *sh,int attnum,int coltype);
#define shape_check_coltype(sh,attnum,coltype) \
        slow_shape_check_coltype(sh,attnum,coltype)

#endif

/* A loadinst is a small data structure specifiying information about what
   to do when loading a datafile. It tells us which line_format to use
   (see later comments on line_formats for more details there).
     ldi->line_format must be one of AUTO_FORMAT WHITESPACE_FORMAT,
     COMMA_FORMAT or BAR_FORMAT

   It tells us whether to ignore records that contain a missing value.

   And if we decide not to ignore records with missing values it tells
    us what default value to use for numeric attributes (symbolic
    attributes get the special value "?") */
typedef struct loadinst
{
  char *missing_string; /* Always assume a string of just space denotes missing, 
                           or a string starting with ?. But if this field is
                           non-null, treat IT as missing also. */
  int line_format;
  bool ignore_missing;
  double missing_numeric_value;
  bool ignore_short;   /* If a record (line of the file) is too short (i.e. with too
                          few tokens) then by default we signal a loading error. However,
                          if "ignore_short" is TRUE we simply ignore that line and continue. */
  bool default_real; /* If an attribute has only a few integer values
                        then by default (and if default_real is FALSE) it
                        is treated as a symbolic attribute. If default_real
                        is TRUE then it's treated as a real number. */
  string_array *these_atts_symbolic; /* This is an array of attribute names. If, during loading,
                                        we encounter an attribute with one of the names in
                                        here, we force it to be symbolic, even if it contains
                                        what look like numeric values. */
  bool all_atts_symbolic;
} loadinst;

/* The 2nd arg must be one of:
    AUTO_FORMAT
    COMMA_FORMAT
    BAR_FORMAT
    WHITESPACE_FORMAT 
   defined in shape.h */
void loadinst_set_line_format(loadinst *ldi,int line_format);

/* Searches the command line for "format XXX" where XXX is one
   of auto comma or space. If doesn't find this, keeps line
   format unchanged */
void loadinst_update_line_format_from_args(loadinst *ldi,int argc,char *argv[]);

/* When the file is loaded, if any attribute has a name that
   appears in attnames then it is loaded as though it were
   symbolic even if all the actual values are numbers. */
void loadinst_set_force_symbolic(loadinst *ldi,string_array *attnames);

/* When the file is loaded, any attribute
   is loaded as though it were
   symbolic even if all the actual values are numbers. */
void loadinst_all_force_symbolic(loadinst* ldi);

/* Adds another attname to the set of names that will be loaded
   as symbolic */
void loadinst_add_force_symbolic(loadinst *ldi,char *attname);

/* Searches the command line for key <tokens>
   where key is defined by the "key" are below and
   <tokens> are one-or-more tokens on the command line
   obeying the command-line set-of-items notation described
   in amdmex.h for mk_string_array_from_args(...).

   Here are examples:

      force_symbolic [ age , gender, id]
      force_symbolic [ age  gender id ]
      force_symbolic [age,gender,id]
      force_symbolic age,gender,id
      force_symbolic "age gender id"
*/
void loadinst_set_force_symbolic_from_args(loadinst *ldi,char *key,
                                           int argc,char *argv[]);

/* Sets the default_real flag. */
void loadinst_set_default_real(loadinst *ldi,bool default_real);

/* Sets the default_real flag from args using key "default_real". */
void loadinst_set_default_real_from_args(loadinst *ldi,int argc,char *argv[]);

/* Gives an explanation of what this particular loadinst will
   do with the data */
void explain_loadinst(loadinst *ldi);

/* Makes a loadinst from the command line arguments using the
   syntax described in the individual functions above */
loadinst *mk_loadinst_from_args(int argc,char *argv[]);

void free_loadinst(loadinst *ldi);

/* Returns TRUE if and only if attribute "attname" has been requested
   to be loaded in symbolic form. */
bool loadinst_name_to_load_symbolic(loadinst *ldi, char *attname);

#define MISSING_STRING "?"

void free_shape(shape *x);

bool shape_equal(shape *s1,shape *s2);

shape *mk_copy_shape(const shape *x);

void fprintf_shape(FILE *s,char *m1,shape *x,char *m2);

string_array *mk_attnames_from_tokens(string_array *tokens);

void get_attnames_and_first_tokens(FILE *s,
                                   string_array **r_attnames,
                                   string_array **r_tokens,
                                   int line_format,
                                   int *line_number,
                                   char **r_errmess);

shape *mk_shape(string_array *attnames); /* Defaults to symbolic *sigh* */

shape *mk_shape_real(string_array *attnames);

int shape_num_atts(const shape *sh);

char *shape_attnum_to_name(const shape *sh,int attnum);

/* Which of the symbolic value of the specified
   (symbolic) attribute has the given name? That's
   what this function returns. If no values have this
   name, the function returns -1 */
int shape_attnum_name_to_value(const shape *sh,int attnum,char *s);

int shape_attnum_to_arity(const shape *sh,int attnum);

bool shape_attnum_to_symbolicp(const shape *sh,int attnum);

char *shape_attnum_value_to_name(const shape *sh,int attnum,int value);

int shape_num_symbolic_atts(const shape *sh);

int shape_num_real_atts(const shape *sh);

void shape_delete_attribute(shape *sh,int attnum);

/* This swaps the descriptions of att1 and att2 in the shape.  
   Precondition -- att1 and att2 are in range and not equal
         to each other. */
void shape_swap_attributes(shape *sh,int att1,int att2);

void shape_rename_attribute(shape *sh,int attnum,char *newname);

/* Warning---does not attempt to keep LEXI order */
void shape_rename_value(shape *sh,int attnum,int value,char *newname);

void shape_delete_value(shape *sh,int attnum,int value);

/* Adds a new attribute (column) to the shape, of type REAL_COL */
void shape_add_real_attribute(shape *sh,char *attname);

bool string_array_is_sorted(string_array *sa);

/* Makes a new symbolic attribute. The arity of the new attribute is
   string_array_size(value_names), and the name of the i'th value is
   string_array_ref(value_names,i).

   The name of the new attribute is attname.
*/
void shape_add_symbolic_attribute(shape *sh,char *attname,
                                  string_array *value_names);

/* It will cause a my_error if you attempt to set major_coltype
   to be UNDEFINED_COL or to the same value as minor_coltype */
void shape_set_coltype(shape *sh,int attnum,
		       int major_coltype,int minor_coltype);


/* Adds the name as a new symbolic value of the attnum'th attribute.
   PRE: shape_symbol_ref_ok(sh,attnum)
 
   NOTE: This does not attempt to ensure the names are in
         lexicographic order.
*/
void shape_add_value_name(shape *sh,int attnum,char *name);

/* Very efficiently changes all the value names of the 
   given (symbolic) attribute. If the i'th value name was
   previously "xxx" it now becomes "<prefix>xxx" */
void shape_valnames_prepend(shape *sh,int attnum,char *prefix);

/* For each name in turn, if the name is not already present in the list of
   symbols, it is added.
   This does NOT attempt to ensure that the order of the values 
   corresponds to lexicographic order of their names. */
/* PRE: shape_symbol_ref_ok(cs,attnum) */
void shape_add_value_names(shape *sh,int attnum,
			       string_array *value_names);

/* Irrespective of previous symbolic values, they are replaced
   with value_names.
   This does NOT attempt to ensure that the order of the values 
   corresponds to lexicographic order of their names. */
/* PRE: shape_symbol_ref_ok(cs,attnum) */
void shape_set_value_names(shape *sh,int attnum,
			       string_array *value_names);

void shape_set_attname(shape *sh,int attnum,char *new_attname);

void save_shape(FILE *s,shape *sh);

/* Except for version 0, the first line of the file should say
   AUTONFastFileFormat Version <n>
   to specify the version number
 */
shape *mk_shape_from_stream(FILE *s,int *line,int *version,char **r_errmess);

FILE *fopen_with_errmess(const char *filename,char *format,char **r_errmess);

#define MISSING_STRING "?"

/* True provided all the symbols in the string array can be parsed
   as numbers and/or are equal to "?" (i.e. MISSING_STRING) */
bool all_entries_are_numeric(string_array *sa);

bool ds_ignore_token(loadinst *ldi,char *token);

bool ds_ignore_tokens(loadinst *ldi,string_array *tokens);

/* A loadinst is a small data structure specifiying information about what
   to do when loading a datafile. It tells us which line_format to use
   (see later comments on line_formats for more details there).
   ldi->line_format must be one of AUTO_FORMAT WHITESPACE_FORMAT or 
   BAR_FORMAT or COMMA_FORMAT.

   It tells us whether to ignore records that contain a missing value.

   And if we decide not to ignore records with missing values it tells
   us what default value to use for numeric attributes (symbolic
   attributes get the special value "?") */
loadinst *mk_default_loadinst(void);

bool loadinst_load_symbolic(loadinst *ldi, char *attname);

bool shape_symbol_ref_ok(const shape *sh,int attnum);

bool shape_real_ref_ok(const shape *sh,int attnum);

void sort_all_symbolic_attribute_values(shape *s);

shape *mk_shape_deduced_from_file(loadinst *ldi,const char *filename,
                                  int *r_num_rows,char **r_errmess);

/* Adds value_name into the list of names of the given attnum,
   inserting at the appropriate position to ensure that the
   values remain in lexicographic order. Returns the small int
   integer value of the new value name. You may assume that if some
   other value_name had small int value v before then afterwards
   it has...

     ...value v if v < result
     ...value v+1 if v >= result

   where result is the result of this function (i.e. the new symbol_value
   of the new value_name)
*/
int shape_insert_value_name(shape *sh,int attnum,char *value_name);

int shape_name_to_attnum(shape* sh,const char *s);

#endif /* #ifndef SHAPE_H */
