/*********************************
  File:     base_itries.h
  Author:   Ricardo Rocha
  Comments: Tries module for ILP
  version:  $ID$
*********************************/



/* --------------------------- */
/*           Defines           */
/* --------------------------- */

#define ITRIES_MODE_NONE      0
#define ITRIES_MODE_INC_POS   1
#define ITRIES_MODE_DEC_POS   2
#define ITRIES_MODE_INC_NEG   3
#define ITRIES_MODE_DEC_NEG   4
#define BASE_TR_DATA_BUCKETS 20



/* --------------------------- */
/*           Structs           */
/* --------------------------- */

typedef struct itrie_entry {
  struct trie_node *top_trie_node;
  struct itrie_data **trie_data_buckets;
  struct itrie_data *traverse_trie_data;
  struct itrie_entry *next;
  struct itrie_entry *previous;
  YAP_Int mode;
  YAP_Int timestamp;
  YAP_Int number_of_buckets;
  YAP_Int traverse_bucket;
} *TrEntry;

#define TrEntry_trie(X)            ((X)->top_trie_node)
#define TrEntry_buckets(X)         ((X)->trie_data_buckets)
#define TrEntry_bucket(X,N)        ((X)->trie_data_buckets + N)
#define TrEntry_traverse_data(X)   ((X)->traverse_trie_data)
#define TrEntry_next(X)            ((X)->next)
#define TrEntry_previous(X)        ((X)->previous)
#define TrEntry_mode(X)            ((X)->mode)
#define TrEntry_timestamp(X)       ((X)->timestamp)
#define TrEntry_num_buckets(X)     ((X)->number_of_buckets)
#define TrEntry_traverse_bucket(X) ((X)->traverse_bucket)

typedef struct itrie_data {
  struct itrie_entry *itrie;
  struct trie_node *leaf_trie_node;
  struct itrie_data *next;
  struct itrie_data *previous;
  YAP_Int pos;
  YAP_Int neg;
  YAP_Int timestamp;
  YAP_Int depth;
} *TrData;

#define TrData_itrie(X)     ((X)->itrie)
#define TrData_leaf(X)      ((X)->leaf_trie_node)
#define TrData_next(X)      ((X)->next)
#define TrData_previous(X)  ((X)->previous)
#define TrData_pos(X)       ((X)->pos)
#define TrData_neg(X)       ((X)->neg)
#define TrData_timestamp(X) ((X)->timestamp)
#define TrData_depth(X)     ((X)->depth)

#define TYPE_TR_ENTRY         struct itrie_entry
#define TYPE_TR_DATA          struct itrie_data
#define SIZEOF_TR_ENTRY       sizeof(TYPE_TR_ENTRY)
#define SIZEOF_TR_DATA        sizeof(TYPE_TR_DATA)
#define SIZEOF_TR_DATA_BUCKET sizeof(TYPE_TR_DATA *)

#define AS_TR_ENTRY_NEXT(ADDR) (TrEntry)((unsigned long int)(ADDR) - sizeof(struct trie_node *) - sizeof(struct itrie_data **) - sizeof(struct itrie_data *))
#define AS_TR_DATA_NEXT(ADDR)  (TrData)((unsigned long int)(ADDR) - sizeof(struct itrie_entry *) - sizeof(struct trie_node *))



/* --------------------------- */
/*           Macros            */
/* --------------------------- */

#define new_itrie_entry(TR_ENTRY, TR_NODE)                                       \
        { new_struct(TR_ENTRY, TYPE_TR_ENTRY, SIZEOF_TR_ENTRY);                  \
          TrEntry_mode(TR_ENTRY) = ITRIES_MODE_NONE;                             \
          TrEntry_timestamp(TR_ENTRY) = -1;                                      \
          TrEntry_num_buckets(TR_ENTRY) = BASE_TR_DATA_BUCKETS;                  \
          new_itrie_buckets(TR_ENTRY, BASE_TR_DATA_BUCKETS);                     \
          TrEntry_trie(TR_ENTRY) = TR_NODE;                                      \
          TrEntry_next(TR_ENTRY) = FIRST_ITRIE;                                  \
          TrEntry_previous(TR_ENTRY) = AS_TR_ENTRY_NEXT(&FIRST_ITRIE);           \
          INCREMENT_MEMORY(ITRIE_ENGINE, SIZEOF_TR_ENTRY);                       \
	}
#define new_itrie_buckets(TR_ENTRY, NUM_BUCKETS)                                 \
        { YAP_Int i; void **ptr;                                                 \
          new_struct(ptr, void *, NUM_BUCKETS * sizeof(void *));                 \
          TrEntry_buckets(TR_ENTRY)  = (TYPE_TR_DATA **) ptr;                    \
          for (i = NUM_BUCKETS; i != 0; i--)                                     \
            *ptr++ = NULL;                                                       \
          INCREMENT_MEMORY(ITRIE_ENGINE, (NUM_BUCKETS) * SIZEOF_TR_DATA_BUCKET); \
        }
#define new_itrie_data(TR_DATA, TR_ENTRY, TR_NODE, POS, NEG, TIME, DEPTH)        \
        { TrData *bucket;                                                        \
          new_struct(TR_DATA, TYPE_TR_DATA, SIZEOF_TR_DATA);                     \
          TrData_pos(TR_DATA) = POS;                                             \
          TrData_neg(TR_DATA) = NEG;                                             \
          TrData_timestamp(TR_DATA) = TIME;                                      \
          TrData_depth(TR_DATA) = DEPTH;                                         \
          TrData_itrie(TR_DATA) = TR_ENTRY;                                      \
          TrData_leaf(TR_DATA) = TR_NODE;                                        \
          if (DEPTH >= TrEntry_num_buckets(TR_ENTRY)) {                          \
            YAP_Int i, new_num_buckets = DEPTH + BASE_TR_DATA_BUCKETS;           \
            bucket = TrEntry_buckets(TR_ENTRY);                                  \
            new_itrie_buckets(TR_ENTRY, new_num_buckets);                        \
            memcpy(TrEntry_buckets(TR_ENTRY), bucket,                            \
                   TrEntry_num_buckets(TR_ENTRY) * SIZEOF_TR_DATA_BUCKET);       \
            free_itrie_buckets(bucket, TrEntry_num_buckets(TR_ENTRY));           \
            bucket = TrEntry_buckets(TR_ENTRY);                                  \
            for (i = 0; i != TrEntry_num_buckets(TR_ENTRY); i++) {               \
               if (*bucket)                                                      \
                 TrData_previous(*bucket) = AS_TR_DATA_NEXT(bucket);             \
                bucket++;                                                        \
	    }                                                                    \
            TrEntry_num_buckets(TR_ENTRY) = new_num_buckets;                     \
	  }                                                                      \
          bucket = TrEntry_bucket(TR_ENTRY, DEPTH);                              \
          TrData_next(TR_DATA) = *bucket;                                        \
          TrData_previous(TR_DATA) = AS_TR_DATA_NEXT(bucket);                    \
          if (*bucket)                                                           \
            TrData_previous(*bucket) = TR_DATA;                                  \
          *bucket = TR_DATA;                                                     \
          INCREMENT_MEMORY(ITRIE_ENGINE, SIZEOF_TR_DATA);                        \
        }
#define update_itrie_data(TR_DATA, TIMESTAMP, ITRIES_MODE)                       \
        if (TrData_timestamp(TR_DATA) != TIMESTAMP) {                            \
          if (ITRIES_MODE == ITRIES_MODE_INC_POS)                                \
            TrData_pos(TR_DATA)++;                                               \
          else if (ITRIES_MODE == ITRIES_MODE_DEC_POS)                           \
            TrData_pos(TR_DATA)--;                                               \
          else if (ITRIES_MODE == ITRIES_MODE_INC_NEG)                           \
            TrData_neg(TR_DATA)++;                                               \
          else if (ITRIES_MODE == ITRIES_MODE_DEC_NEG)                           \
            TrData_neg(TR_DATA)--;                                               \
          TrData_timestamp(TR_DATA) = TIMESTAMP;                                 \
        } 



#define free_itrie_entry(STR)                                                    \
        { free_itrie_buckets(TrEntry_buckets(STR), TrEntry_num_buckets(STR));    \
          free_struct(STR);                                                      \
          DECREMENT_MEMORY(ITRIE_ENGINE, SIZEOF_TR_ENTRY);                       \
        }
#define free_itrie_buckets(STR, NUM_BUCKETS)                                     \
        { free_struct(STR);                                                      \
          DECREMENT_MEMORY(ITRIE_ENGINE, (NUM_BUCKETS) * SIZEOF_TR_DATA_BUCKET); \
        }
#define free_itrie_data(STR)                                                     \
        { free_struct(STR);                                                      \
          DECREMENT_MEMORY(ITRIE_ENGINE, SIZEOF_TR_DATA);                        \
	}



/* --------------------------- */
/*             API             */
/* --------------------------- */

inline void     itrie_init_module(void);
inline void     itrie_data_save(TrNode node, FILE *file);
inline void     itrie_data_load(TrNode node, YAP_Int depth, FILE *file);
inline void     itrie_data_print(TrNode node);
inline void     itrie_data_copy(TrNode node_dest, TrNode node_source);
inline void     itrie_data_destruct(TrNode node);
inline void     itrie_data_add(TrNode node_dest, TrNode node_source);
inline void     itrie_data_subtract(TrNode node_dest, TrNode node_source);
inline TrEntry  itrie_open(void);
inline void     itrie_close(TrEntry itrie);
inline void     itrie_close_all(void);
inline void     itrie_set_mode(TrEntry itrie, YAP_Int mode);
inline YAP_Int  itrie_get_mode(TrEntry itrie);
inline void     itrie_set_timestamp(TrEntry itrie, YAP_Int timestamp);
inline YAP_Int  itrie_get_timestamp(TrEntry itrie);
inline void     itrie_put_entry(TrEntry itrie, YAP_Term entry);
inline void     itrie_update_entry(TrEntry itrie, YAP_Term entry);
inline TrData   itrie_check_entry(TrEntry itrie, YAP_Term entry);
inline YAP_Term itrie_get_entry(TrData data);
inline void     itrie_get_data(TrData data, YAP_Int *pos, YAP_Int *neg, YAP_Int *timestamp);
inline TrData   itrie_traverse_init(TrEntry itrie);
inline TrData   itrie_traverse_cont(TrEntry itrie);
inline void     itrie_remove_entry(TrData data);
inline void     itrie_remove_subtree(TrData data);
inline void     itrie_add(TrEntry itrie_dest, TrEntry itrie_source);
inline void     itrie_subtract(TrEntry itrie_dest, TrEntry itrie_source);
inline void     itrie_join(TrEntry itrie_dest, TrEntry itrie_source);
inline void     itrie_intersect(TrEntry itrie_dest, TrEntry itrie_source);
inline YAP_Int  itrie_count_join(TrEntry itrie1, TrEntry itrie2);
inline YAP_Int  itrie_count_intersect(TrEntry itrie1, TrEntry itrie2);
inline void     itrie_save(TrEntry itrie, FILE *file);
inline void     itrie_save_as_trie(TrEntry itrie, FILE *file);
inline TrEntry  itrie_load(FILE *file);
inline void     itrie_stats(YAP_Int *memory, YAP_Int *tries, YAP_Int *entries, YAP_Int *nodes);
inline void     itrie_max_stats(YAP_Int *memory, YAP_Int *tries, YAP_Int *entries, YAP_Int *nodes);
inline void     itrie_usage(TrEntry itrie, YAP_Int *entries, YAP_Int *nodes, YAP_Int *virtual_nodes);
inline void     itrie_print(TrEntry itrie);