#ifndef MOE_MBBTRI_H
#define MOE_MBBTRI_H

#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <altmalloc.h>

enum {
  bt_node,
  bt_1,
  bt_n,
  bt_v,
  bt_nkinds,
  bt_failure = bt_nkinds,
};

typedef unsigned char bt_result_t;

#define BTRI_OP_WR (1 << 0)
#define BTRI_OP_ADD (1 << 1)
#define BTRI_OP_SUB (1 << 2)
#define BTRI_OP_N2N (1 << 3)

#define BTRI_O2P(t, base, off) ((t)((unsigned char *)(base) + (ptrdiff_t)(off)))

typedef struct btri_key_cursor_st {
  const void *base;
  ptrdiff_t n;
} btri_key_cursor_t;

typedef struct btri_desc_st {
  ptrdiff_t off_pos, off_count, off_max;
  ptrdiff_t off_type[2];
  ptrdiff_t off_key[2];
  ptrdiff_t off_key_n[2];
  ptrdiff_t off_value[2];
  size_t alpha_bit;
  unsigned int (*alpha_filter)(struct btri_desc_st *, const void *);
  size_t node_size;
  int (*key_cmp)(struct btri_desc_st *, ptrdiff_t *, btri_key_cursor_t *, btri_key_cursor_t *);
  void (*key_fetch)(struct btri_desc_st *, void *, ptrdiff_t, btri_key_cursor_t *);
  int (*key_fetch_and_cmp)(struct btri_desc_st *, ptrdiff_t *, btri_key_cursor_t *, void *, ptrdiff_t);
  void *(*key_store)(struct btri_desc_st *, btri_key_cursor_t *, void *, ptrdiff_t);
  void *(*new_space)(struct btri_desc_st *, size_t);
  void (*free_space)(struct btri_desc_st *, void *);
  void *(*init_node)(struct btri_desc_st *, void *);
  void *(*init_key)(struct btri_desc_st *, void *);
  void (*free_key)(struct btri_desc_st *, void *, ptrdiff_t);
  void (*free_node)(struct btri_desc_st *, void *);
} btri_desc_t;

typedef struct btri_string_tab_st {
  ptrdiff_t pos;
  bt_result_t type[2];
  btri_key_cursor_t key[2];
  void *value[2];
} btri_string_tab_t;

typedef struct btri_uint_tab_st {
  ptrdiff_t pos;
  bt_result_t type[2];
  char key_n[2];
  unsigned int key[2];
  void *value[2];
} btri_uint_tab_t;

typedef struct btri_uint_opt_tab_st {
  btri_uint_tab_t x;
  unsigned int count, max;
} btri_uint_opt_tab_t;

extern size_t bt_enc(btri_uint_opt_tab_t *tab, unsigned int lcount, unsigned int *v);
extern bt_result_t bt_search(unsigned int key, unsigned int *node, unsigned int *p_value);
extern void *btri_new_node(btri_desc_t *desc);
extern int btri_cmp(btri_desc_t *desc, ptrdiff_t *p_b, btri_key_cursor_t *x, btri_key_cursor_t *y);
extern void btri_key_fetch_cursor(btri_desc_t *desc, void *node, ptrdiff_t i, btri_key_cursor_t *y);
extern void btri_key_fetch_uint(btri_desc_t *desc, void *node, ptrdiff_t i, btri_key_cursor_t *y);
extern int btri_key_fetch_cursor_and_cmp(btri_desc_t *desc, ptrdiff_t *, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern int btri_fetch_uchar_and_ci_cmp(btri_desc_t *desc, ptrdiff_t *p_b, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern int btri_fetch_uchar_and_cmp(btri_desc_t *desc, ptrdiff_t *p_b, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern int btri_fetch_uint_and_cmp(btri_desc_t *desc, ptrdiff_t *p_b, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern void *btri_key_store_cursor(btri_desc_t *desc, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern void *btri_key_store_uint(btri_desc_t *desc, btri_key_cursor_t *x, void *node, ptrdiff_t i);
extern bt_result_t btri_search(btri_desc_t *desc, int op, btri_key_cursor_t *key, void *node, void **p_value);
extern bt_result_t btri_fast_search_mem(const char *key, ptrdiff_t bytes, btri_string_tab_t *node, void **p_value);
#define btri_fast_search_str(k, no, p_val) (btri_fast_search_mem((k), strlen((k)), (no), (p_val)))
extern bt_result_t btri_fast_ci_search_mem(const char *key, ptrdiff_t bytes, btri_string_tab_t *node, void **p_value);
#define btri_fast_ci_search_str(k, no, p_val) (btri_fast_ci_search_mem((k), strlen((k)), (no), (p_val)))
extern bt_result_t btri_fast_search_uint(unsigned int key, btri_uint_tab_t *node, void **p_value);
#define btri_fast_search_uint_opt(key, node, p_value) btri_fast_search_uint((key), &((btri_uint_opt_tab_t *)(node))->x, (p_value))
extern void btri_free_recursively(btri_desc_t *desc, void *node);
extern void *btri_copy(btri_desc_t *desc, void *old);
extern void *btri_merge(btri_desc_t *desc, void *x, void *y);
extern int btri_map(btri_desc_t *desc, void *root, int (*func)(btri_desc_t *desc, void **p_value, void *arg), void *arg);
extern int btri_map_max_smaller(btri_desc_t *desc, ptrdiff_t b, btri_key_cursor_t *key, void *root,
				int (*func)(btri_desc_t *desc, void **p_value, void *arg), void *arg);
extern int btri_map_min_larger(btri_desc_t *desc, ptrdiff_t b, btri_key_cursor_t *key, void *root,
			       int (*func)(btri_desc_t *desc, void **p_value, void *arg), void *arg);
extern int btri_map_region(btri_desc_t *desc,
			   ptrdiff_t beg_b, btri_key_cursor_t *beg, ptrdiff_t end_b, btri_key_cursor_t *end, void *node,
			   int (*func)(btri_desc_t *desc, void **p_value, void *arg), void *arg);
extern bt_result_t btri_search_str(btri_desc_t *desc, int op, const char *key, void *node, void **p_value);
extern bt_result_t btri_search_mem(btri_desc_t *desc, int op, const void *key, size_t len, void *node, void **p_value);
extern bt_result_t btri_search_uint(btri_desc_t *desc, int op, unsigned int key, void *node, void **p_value);
extern bt_result_t btri_add_uint_n_to_1(btri_desc_t *desc, unsigned int beg, unsigned int end, void *node, void *value);
extern bt_result_t btri_add_uint_n_to_n(btri_desc_t *desc, unsigned int beg, unsigned int end, void *node, unsigned int value);
extern void *btri_uint_optimize(btri_desc_t *desc, void *node, bt_result_t *p_t, char *p_e,
				unsigned int *p_count, unsigned int *p_max, int uint_value);
extern unsigned int *btri_uint_tab_to_vector(btri_desc_t *desc, unsigned int mask, unsigned int nvals, void *node);
extern size_t btri_pack_uint_tab(btri_desc_t *desc, void *node, unsigned int *dest);
extern unsigned int btri_uint_filter(btri_desc_t *desc, const void *p);
extern unsigned int btri_uint_lower_filter(btri_desc_t *desc, const void *p);
extern unsigned int btri_uchar_filter(btri_desc_t *desc, const void *p);
extern unsigned int btri_uchar_lower_filter(btri_desc_t *desc, const void *p);
extern void *btri_malloc(btri_desc_t *desc, size_t size);
extern void *btri_realloc(btri_desc_t *desc, void *old, size_t size);
extern void btri_free_key(btri_desc_t *desc, void *key, ptrdiff_t size);
extern void btri_free_node(btri_desc_t *desc, void *node);
extern btri_desc_t btri_string_ci_tab_desc;
extern btri_desc_t btri_string_tab_desc;
extern btri_desc_t btri_uint_tab_desc;
extern btri_desc_t btri_uint_opt_tab_desc;

#endif
