/*************************************************************************
*									 *
*	 YAP Prolog 	%W% %G%		*
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		clause.h						 *
* Last rev:								 *
* mods:									 *
* comments:	clause info						 *
*									 *
*************************************************************************/

#ifndef CLAUSE_H
#define CLAUSE_H 1

#include "Yatom.h"
#include "YapHeap.h"

/* consulting files */

typedef union CONSULT_OBJ {
  const unsigned char *filename;
  int mode;
  Prop p;
  UInt c;
  Term r;
} consult_obj;

/* Either we are assembling clauses or indexing code */

#define ASSEMBLING_CLAUSE 0
#define ASSEMBLING_INDEX 1
#define ASSEMBLING_EINDEX 2

#define NextDynamicClause(X) (((yamop *)X)->y_u.Otapl.d)

#define PredFirstClause 0
#define PredMiddleClause 1
#define PredLastClause 2

typedef struct logic_upd_index {
  CELL ClFlags;
  UInt ClRefCount;
#if defined(YAPOR) || defined(THREADS)
/* A lock for manipulating the clause */
//  lockvar          ClLock;
#endif
  UInt ClSize;
  struct logic_upd_index *ParentIndex;
  struct logic_upd_index *SiblingIndex;
  struct logic_upd_index *PrevSiblingIndex;
  struct logic_upd_index *ChildIndex;
  /* The instructions, at least one of the form sl */
  PredEntry *ClPred;
  yamop ClCode[MIN_ARRAY];
} LogUpdIndex;

/* The ordering of the first 3 fields should be compatible with dbrefs */
typedef struct logic_upd_clause {
  Functor Id; /* allow pointers to this struct to id  */
              /*   as dbref                           */
  /* A set of flags describing info on the clause */
  /* A set of flags describing info on the clause */
  CELL ClFlags;
#if defined(YAPOR) || defined(THREADS)
/* A lock for manipulating the clause */
//  lockvar          ClLock;
#endif
  UInt ClSize;
  /* extra clause information for logical update indices and facts */
  /* indices that may still backtrack to this clause */
  UInt ClRefCount;
  /* data for clauses  with environments */
  yamop *ClExt;
  union {
    DBTerm *ClSource;
    Int ClLine;
  } lusl;
  /* doubly linked list of clauses */
  struct logic_upd_clause *ClPrev, *ClNext;
  /* parent pointer */
  PredEntry *ClPred;
  UInt ClTimeStart, ClTimeEnd;
  /* The instructions, at least one of the form sl */
  yamop ClCode[MIN_ARRAY];
} LogUpdClause;

#include "inline-only.h"
INLINE_ONLY inline EXTERN int VALID_TIMESTAMP(UInt, struct logic_upd_clause *);

INLINE_ONLY inline EXTERN int VALID_TIMESTAMP(UInt timestamp,
                                              struct logic_upd_clause *cl) {
  return IN_BETWEEN(cl->ClTimeStart, timestamp, cl->ClTimeEnd);
}

typedef struct dynamic_clause {
  /* A set of flags describing info on the clause */
  CELL ClFlags;
#if defined(YAPOR) || defined(THREADS)
  /* A lock for manipulating the clause */
  lockvar ClLock;
#endif
  UInt ClSize;
  Int ClLine;
  UInt ClRefCount;
  yamop *ClPrevious; /* immediate update clause */
  /* The instructions, at least one of the form sl */
  yamop ClCode[MIN_ARRAY];
} DynamicClause;

typedef struct static_index {
  /* A set of flags describing info on the clause */
  CELL ClFlags;
  UInt ClSize;
  struct static_index *SiblingIndex;
  struct static_index *ChildIndex;
  /* The instructions, at least one of the form sl */
  PredEntry *ClPred;
  yamop ClCode[MIN_ARRAY];
} StaticIndex;

typedef struct static_clause {
  /* A set of flags describing info on the clause */
  CELL ClFlags;
  UInt ClSize;
  union {
    DBTerm *ClSource;
    Int ClLine;
  } usc;
  struct static_clause *ClNext;
  /* The instructions, at least one of the form sl */
  yamop ClCode[MIN_ARRAY];
} StaticClause;

typedef struct static_mega_clause {
  /* A set of flags describing info on the clause */
  CELL ClFlags;
  UInt ClSize;
  PredEntry *ClPred;
  UInt ClItemSize;
  Int ClLine;
  struct static_mega_clause *ClNext;
  /* The instructions, at least one of the form sl */
  yamop ClCode[MIN_ARRAY];
} MegaClause;

typedef union clause_obj {
  struct logic_upd_clause luc;
  struct logic_upd_index lui;
  struct dynamic_clause ic;
  struct static_clause sc;
  struct static_mega_clause mc;
  struct static_index si;
} ClauseUnion;

typedef union clause_ptr {
  struct logic_upd_clause *luc;
  struct logic_upd_index *lui;
  struct dynamic_clause *ic;
  struct static_clause *sc;
  struct static_mega_clause *mc;
  struct static_index *si;
} ClausePointer;

typedef struct index_t {
  struct index_t *next, *prev;
  UInt nels;
  UInt arity;
  PredEntry *ap;
  CELL bmap;
  int is_key;
  int is_udi;
  UInt ncollisions;
  UInt max_col_count;
  UInt ntrys;
  UInt nentries;
  UInt hsize;
  BITS32 *key;
  CELL *cls, *bcls;
  BITS32 *links;
  size_t size;
  yamop *code;
  BITS32 *udi_data;
  void *udi_first, *udi_next;
  UInt udi_free_args;
  UInt udi_arg;
} Index_t;

INLINE_ONLY EXTERN inline BITS32 EXO_ADDRESS_TO_OFFSET(struct index_t *it,
                                                       CELL *ptr);

INLINE_ONLY EXTERN inline BITS32 EXO_ADDRESS_TO_OFFSET(struct index_t *it,
                                                       CELL *ptr) {
  return (ptr - it->cls) / it->arity + 1;
}

INLINE_ONLY EXTERN inline CELL *EXO_OFFSET_TO_ADDRESS(struct index_t *it,
                                                      BITS32 off);

INLINE_ONLY EXTERN inline CELL *EXO_OFFSET_TO_ADDRESS(struct index_t *it,
                                                      BITS32 off) {
  if (off == 0L)
    return (CELL *)NULL;
  return (it->cls) + (off - 1) * it->arity;
}

INLINE_ONLY EXTERN inline BITS32 ADDRESS_TO_LINK(struct index_t *it,
                                                 BITS32 *ptr);

INLINE_ONLY EXTERN inline BITS32 ADDRESS_TO_LINK(struct index_t *it,
                                                 BITS32 *ptr) {
  return ptr - it->links;
}

INLINE_ONLY EXTERN inline BITS32 *LINK_TO_ADDRESS(struct index_t *it,
                                                  BITS32 off);

INLINE_ONLY EXTERN inline BITS32 *LINK_TO_ADDRESS(struct index_t *it,
                                                  BITS32 off) {
  return it->links + off;
}

typedef void (*CRefitExoIndex)(struct index_t **ip, UInt b[] USES_REGS);
typedef yamop *(*CEnterExoIndex)(struct index_t *it USES_REGS);
typedef int (*CRetryExoIndex)(struct index_t *it USES_REGS);

typedef struct dbterm_list {
  /* a list of dbterms associated with a clause */
  DBTerm *dbterms;
  yamop *clause_code;
  PredEntry *p;
  struct dbterm_list *next_dbl;
} DBTermList;

#define ClauseCodeToDynamicClause(p)                                           \
  ((DynamicClause *)((CODEADDR)(p) - (CELL)(((DynamicClause *)NULL)->ClCode)))
#define ClauseCodeToStaticClause(p)                                            \
  ((StaticClause *)((CODEADDR)(p) - (CELL)(((StaticClause *)NULL)->ClCode)))
#define ClauseCodeToLogUpdClause(p)                                            \
  ((LogUpdClause *)((CODEADDR)(p) - (CELL)(((LogUpdClause *)NULL)->ClCode)))
#define ClauseCodeToMegaClause(p)                                              \
  ((MegaClause *)((CODEADDR)(p) - (CELL)(((MegaClause *)NULL)->ClCode)))
#define ClauseCodeToLogUpdIndex(p)                                             \
  ((LogUpdIndex *)((CODEADDR)(p) - (CELL)(((LogUpdIndex *)NULL)->ClCode)))
#define ClauseCodeToStaticIndex(p)                                             \
  ((StaticIndex *)((CODEADDR)(p) - (CELL)(((StaticIndex *)NULL)->ClCode)))

#define ClauseFlagsToDynamicClause(p) ((DynamicClause *)(p))
#define ClauseFlagsToLogUpdClause(p)                                           \
  ((LogUpdClause *)((CODEADDR)(p) - (CELL)(&(((LogUpdClause *)NULL)->ClFlags))))
#define ClauseFlagsToLogUpdIndex(p)                                            \
  ((LogUpdIndex *)((CODEADDR)(p) - (CELL)(&(((LogUpdIndex *)NULL)->ClFlags))))
#define ClauseFlagsToStaticClause(p) ((StaticClause *)(p))

#define DynamicFlags(X) (ClauseCodeToDynamicClause(X)->ClFlags)

#define DynamicLock(X) (ClauseCodeToDynamicClause(X)->ClLock)

#if MULTIPLE_STACKS
#define INIT_CLREF_COUNT(X) (X)->ClRefCount = 0
#define INC_CLREF_COUNT(X) (X)->ClRefCount++
#define DEC_CLREF_COUNT(X) (X)->ClRefCount--

#define CL_IN_USE(X) ((X)->ClRefCount)
#else
#define INIT_CLREF_COUNT(X)
#define INC_CLREF_COUNT(X)
#define DEC_CLREF_COUNT(X)
#define CL_IN_USE(X) ((X)->ClFlags & InUseMask || (X)->ClRefCount)
#endif

/* amasm.c */
wamreg Yap_emit_x(CELL);
COUNT Yap_compile_cmp_flags(PredEntry *);
void Yap_InitComma(void);

/* cdmgr.c */
void Yap_IPred(PredEntry *, UInt, yamop *);
bool Yap_addclause(Term, yamop *, Term, Term, Term *);
void Yap_add_logupd_clause(PredEntry *, LogUpdClause *, int);
void Yap_kill_iblock(ClauseUnion *, ClauseUnion *, PredEntry *);
void Yap_EraseStaticClause(StaticClause *, PredEntry *, Term);
ClauseUnion *Yap_find_owner_index(yamop *, PredEntry *);

/* dbase.c */
void Yap_ErCl(DynamicClause *);
void Yap_ErLogUpdCl(LogUpdClause *);
void Yap_ErLogUpdIndex(LogUpdIndex *);
Int Yap_Recordz(Atom, Term);
Int Yap_db_nth_recorded(PredEntry *, Int USES_REGS);
Int Yap_unify_immediate_ref(DBRef ref USES_REGS);

/* exec.c */
Term Yap_cp_as_integer(choiceptr);

/* index.c */
yamop *Yap_PredIsIndexable(PredEntry *, UInt, yamop *);
yamop *Yap_ExpandIndex(PredEntry *, UInt);
void Yap_CleanUpIndex(struct logic_upd_index *);
void Yap_CleanKids(struct logic_upd_index *);
void Yap_AddClauseToIndex(PredEntry *, yamop *, int);
void Yap_RemoveClauseFromIndex(PredEntry *, yamop *);
LogUpdClause *Yap_NthClause(PredEntry *, Int);
LogUpdClause *Yap_FollowIndexingCode(PredEntry *, yamop *, Term *, yamop *,
                                     yamop *);

/* exo.c */
yamop *Yap_ExoLookup(PredEntry *ap USES_REGS);
CELL Yap_NextExo(choiceptr cpt, struct index_t *it);

#
#if USE_THREADED_CODE

#define OP_HASH_SIZE 2048

INLINE_ONLY inline EXTERN int rtable_hash_op(OPCODE opc, int hash_mask);

INLINE_ONLY inline EXTERN int rtable_hash_op(OPCODE opc, int hash_mask) {
  return ((((CELL)opc) >> 3) & hash_mask);
}

INLINE_ONLY inline EXTERN op_numbers Yap_op_from_opcode(OPCODE opc);

/* given an opcode find the corresponding opnumber. This should make
   switches on ops a much easier operation */
INLINE_ONLY inline EXTERN op_numbers Yap_op_from_opcode(OPCODE opc) {
  int j = rtable_hash_op(opc, OP_HASH_SIZE - 1);

  while (OP_RTABLE[j].opc != opc) {
    if (!OP_RTABLE[j].opc)
      return _Nstop;
    if (j == OP_HASH_SIZE - 1) {
      j = 0;
    } else {
      j++;
    }
  }
  return OP_RTABLE[j].opnum;
}
#else
static inline op_numbers Yap_op_from_opcode(OPCODE opc) {
  return ((op_numbers)opc);
}
#endif /* USE_THREADED_CODE */

#if defined(YAPOR) || defined(THREADS)
static inline int same_lu_block(yamop **, yamop *);

static inline int same_lu_block(yamop **paddr, yamop *p) {
  yamop *np = *paddr;
  if (np != p) {
    OPCODE jmp_op = Yap_opcode(_jump_if_nonvar);

    while (np->opc == jmp_op) {
      np = NEXTOP(np, xll);
      if (np == p)
        return TRUE;
    }
    return FALSE;
  } else {
    return TRUE;
  }
}
#endif

#define Yap_MkStaticRefTerm(cp, ap) __Yap_MkStaticRefTerm((cp), (ap)PASS_REGS)

static inline Term __Yap_MkStaticRefTerm(StaticClause *cp,
                                         PredEntry *ap USES_REGS) {
  Term t[2];
  t[0] = MkIntegerTerm((Int)cp);
  t[1] = MkIntegerTerm((Int)ap);
  return Yap_MkApplTerm(FunctorStaticClause, 2, t);
}

static inline StaticClause *Yap_ClauseFromTerm(Term t) {
  return (StaticClause *)IntegerOfTerm(ArgOfTerm(1, t));
}

#define Yap_MkMegaRefTerm(ap, ipc) __Yap_MkMegaRefTerm((ap), (ipc)PASS_REGS)

static inline Term __Yap_MkMegaRefTerm(PredEntry *ap, yamop *ipc USES_REGS) {
  Term t[2];
  t[0] = MkIntegerTerm((Int)ap);
  t[1] = MkIntegerTerm((Int)ipc);
  return Yap_MkApplTerm(FunctorMegaClause, 2, t);
}

static inline yamop *Yap_MegaClauseFromTerm(Term t) {
  return (yamop *)IntegerOfTerm(ArgOfTerm(2, t));
}

static inline PredEntry *Yap_MegaClausePredicateFromTerm(Term t) {
  return (PredEntry *)IntegerOfTerm(ArgOfTerm(1, t));
}

#define Yap_MkExoRefTerm(ap, i) __Yap_MkExoRefTerm((ap), (i)PASS_REGS)

static inline Term __Yap_MkExoRefTerm(PredEntry *ap, Int i USES_REGS) {
  Term t[2];
  t[0] = MkIntegerTerm((Int)ap);
  t[1] = MkIntegerTerm((Int)i);
  return Yap_MkApplTerm(FunctorExoClause, 2, t);
}

static inline Int Yap_ExoClauseFromTerm(Term t) {
  return IntegerOfTerm(ArgOfTerm(2, t));
}

static inline PredEntry *Yap_ExoClausePredicateFromTerm(Term t) {
  return (PredEntry *)IntegerOfTerm(ArgOfTerm(1, t));
}

/******************************************************************

                        EXECUTING PROLOG CLAUSES

******************************************************************/

bool Yap_search_for_static_predicate_in_use(PredEntry *p,
                                            bool check_everything);

static inline bool Yap_static_in_use(PredEntry *p, bool check_everything) {
#if defined(YAPOR) || defined(THREADS)
  return TRUE;
#else
  pred_flags_t pflags = p->PredFlags;
  if (pflags & (DynamicPredFlag | LogUpdatePredFlag)) {
    return FALSE;
  }
  if (STATIC_PREDICATES_MARKED) {
    return (p->PredFlags & InUsePredFlag);
  } else {
    /* This code does not work for YAPOR or THREADS!!!!!!!! */
    return Yap_search_for_static_predicate_in_use(p, check_everything);
  }
#endif

#define DEAD_REF(ref) FALSE
}

typedef enum {
  FIND_PRED_FROM_ANYWHERE,
  FIND_PRED_FROM_CP,
  FIND_PRED_FROM_ENV
} find_pred_type;

Int Yap_PredForCode(yamop *, find_pred_type, Atom *, UInt *, Term *);
PredEntry *Yap_PredEntryForCode(yamop *, find_pred_type, void **, void **);
LogUpdClause *Yap_new_ludbe(Term, PredEntry *, UInt);
Term Yap_LUInstance(LogUpdClause *, UInt);

/* udi.c */
int Yap_new_udi_clause(PredEntry *, yamop *, Term);
yamop *Yap_udi_search(PredEntry *);

Term Yap_bug_location(yamop *p, yamop *cp, choiceptr b_ptr, CELL *env);
Term Yap_pc_location(yamop *p, choiceptr b_ptr, CELL *env);
Term Yap_env_location(yamop *p, choiceptr b_ptr, CELL *env, Int ignore_first);

#if LOW_PROF
void Yap_InformOfRemoval(void *);
void Yap_dump_code_area_for_profiler(void);
#else
#define Yap_InformOfRemoval(X)
#endif

#endif