2019-01-30 15:24:06 +00:00
|
|
|
/*************************************************************************
|
2019-02-01 13:14:33 +00:00
|
|
|
* *
|
|
|
|
* YAP Prolog *
|
|
|
|
* *
|
|
|
|
* Yap Prolog was developed at NCCUP - Universidade do Porto *
|
|
|
|
* *
|
|
|
|
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
|
|
|
|
* *
|
|
|
|
**************************************************************************
|
|
|
|
* *
|
|
|
|
* File: utilpreds.c * Last rev: 4/03/88
|
|
|
|
** mods: * comments: new utility predicates for YAP *
|
|
|
|
* *
|
|
|
|
*************************************************************************/
|
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
/**
|
|
|
|
* @file C/terms.c
|
|
|
|
*
|
|
|
|
* @brief applications of the tree walker pattern.
|
|
|
|
*
|
|
|
|
* @addtogroup Terms
|
2019-02-01 13:14:33 +00:00
|
|
|
*
|
2019-01-30 15:24:06 +00:00
|
|
|
* @{
|
2019-02-01 13:14:33 +00:00
|
|
|
*
|
2019-01-30 15:24:06 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "absmi.h"
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
#include "YapHeap.h"
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define debug_pop_text_stack(l) [ if (to_visit != to_visit0) printf("%d\n",__LINE__); pop_text_stack(l) \
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
2019-02-06 00:08:15 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
#include "attvar.h"
|
2019-02-01 13:14:33 +00:00
|
|
|
#include "yapio.h"
|
2019-01-30 15:24:06 +00:00
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
#include "string.h"
|
|
|
|
#endif
|
|
|
|
|
2019-02-17 23:19:26 +00:00
|
|
|
extern int cs[10];
|
|
|
|
|
|
|
|
int cs[10];
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
static inline void clean_tr(tr_fr_ptr TR0 USES_REGS) {
|
2019-01-30 15:24:06 +00:00
|
|
|
tr_fr_ptr pt0 = TR;
|
|
|
|
while (pt0 != TR0) {
|
|
|
|
Term p = TrailTerm(--pt0);
|
|
|
|
if (IsApplTerm(p)) {
|
|
|
|
CELL *pt = RepAppl(p);
|
|
|
|
#ifdef FROZEN_STACKS
|
|
|
|
pt[0] = TrailVal(pt0);
|
|
|
|
#else
|
|
|
|
pt[0] = TrailTerm(pt0 - 1);
|
2019-02-01 13:14:33 +00:00
|
|
|
pt0--;
|
2019-01-30 15:24:06 +00:00
|
|
|
#endif /* FROZEN_STACKS */
|
|
|
|
} else {
|
|
|
|
RESET_VARIABLE(p);
|
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
TR = TR0;
|
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
//#define CELL *pt0, *pt0_end, *ptf;
|
|
|
|
//} non_singletons_t;
|
2019-02-09 09:43:26 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define IS_VISIT_MARKER \
|
2019-02-17 23:19:26 +00:00
|
|
|
(IsAtomTerm(d0) && AtomOfTerm(d0) >= (Atom)to_visit0 && \
|
|
|
|
AtomOfTerm(d0) <= (Atom)to_visit)
|
2019-02-11 09:28:46 +00:00
|
|
|
|
2019-02-17 23:19:26 +00:00
|
|
|
#define VISIT_MARKER MkAtomTerm((Atom)to_visit)
|
2019-02-03 21:35:12 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
typedef struct {
|
2019-02-01 13:14:33 +00:00
|
|
|
Term old_var;
|
|
|
|
Term new_var;
|
|
|
|
} * vcell;
|
2019-02-11 09:28:46 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
typedef struct non_single_struct_t {
|
|
|
|
CELL *ptd0;
|
|
|
|
CELL d0;
|
2019-02-04 10:42:23 +00:00
|
|
|
CELL *pt0, *pt0_end, *ptf;
|
2019-01-30 15:24:06 +00:00
|
|
|
} non_singletons_t;
|
2019-02-11 09:28:46 +00:00
|
|
|
|
|
|
|
#define WALK_COMPLEX_TERM__(LIST0, STRUCT0, PRIMI0) \
|
2019-02-14 00:38:14 +00:00
|
|
|
\
|
2019-05-03 12:40:47 +01:00
|
|
|
reset:\
|
|
|
|
lvl = push_text_stack();\
|
|
|
|
to_visit0 = Malloc(auxsz); \
|
|
|
|
pt0 = pt0_; pt0_end = pt0_end_; \
|
|
|
|
to_visit = to_visit0, \
|
|
|
|
to_visit_max = to_visit + auxsz/sizeof(struct non_single_struct_t);\
|
|
|
|
\
|
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256) { \
|
2019-03-15 12:38:09 +00:00
|
|
|
/* Trail overflow */\
|
|
|
|
goto trail_overflow;\
|
|
|
|
}\
|
|
|
|
if (HR + 1024 > ASP) { \
|
|
|
|
goto global_overflow;\
|
|
|
|
}\
|
2019-02-14 00:38:14 +00:00
|
|
|
while (to_visit >= to_visit0) { \
|
2019-02-11 09:28:46 +00:00
|
|
|
CELL d0; \
|
|
|
|
CELL *ptd0; \
|
2019-02-14 00:38:14 +00:00
|
|
|
restart: \
|
|
|
|
while (pt0 < pt0_end) { \
|
|
|
|
++pt0; \
|
|
|
|
ptd0 = pt0; \
|
|
|
|
d0 = *ptd0; \
|
|
|
|
list_loop: \
|
2019-02-11 09:28:46 +00:00
|
|
|
/*fprintf(stderr, "%ld at %s\n", to_visit - to_visit0, __FUNCTION__);*/ \
|
2019-02-14 00:38:14 +00:00
|
|
|
deref_head(d0, var_in_term_unk); \
|
|
|
|
var_in_term_nvar : { \
|
|
|
|
if (IsPairTerm(d0)) { \
|
|
|
|
if (to_visit + 32 >= to_visit_max) { \
|
|
|
|
goto aux_overflow; \
|
|
|
|
} \
|
|
|
|
ptd0 = RepPair(d0); \
|
|
|
|
d0 = ptd0[0]; \
|
|
|
|
LIST0; \
|
|
|
|
if (IS_VISIT_MARKER) \
|
|
|
|
goto restart; \
|
|
|
|
to_visit->pt0 = pt0; \
|
|
|
|
to_visit->pt0_end = pt0_end; \
|
|
|
|
to_visit->ptd0 = ptd0; \
|
|
|
|
to_visit->d0 = d0; \
|
|
|
|
to_visit++; \
|
|
|
|
*ptd0 = VISIT_MARKER; \
|
|
|
|
pt0 = ptd0; \
|
|
|
|
pt0_end = pt0 + 1; \
|
|
|
|
goto list_loop; \
|
|
|
|
} else if (IsApplTerm(d0)) { \
|
|
|
|
register Functor f; \
|
2019-02-11 09:28:46 +00:00
|
|
|
/* store the terms to visit */ \
|
2019-02-14 00:38:14 +00:00
|
|
|
ptd0 = RepAppl(d0); \
|
|
|
|
f = (Functor)(d0 = *ptd0); \
|
|
|
|
if (IsExtensionFunctor(f)) {\
|
|
|
|
continue;\
|
|
|
|
}\
|
|
|
|
\
|
|
|
|
if (to_visit + 32 >= to_visit_max) { \
|
|
|
|
goto aux_overflow; \
|
|
|
|
} \
|
|
|
|
STRUCT0; \
|
|
|
|
if (IS_VISIT_MARKER) { \
|
|
|
|
\
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
to_visit->pt0 = pt0; \
|
|
|
|
to_visit->pt0_end = pt0_end; \
|
|
|
|
to_visit->ptd0 = ptd0; \
|
|
|
|
to_visit->d0 = d0; \
|
|
|
|
to_visit++; \
|
|
|
|
\
|
|
|
|
*ptd0 = VISIT_MARKER; \
|
|
|
|
Term d1 = ArityOfFunctor(f); \
|
|
|
|
pt0 = ptd0; \
|
|
|
|
pt0_end = ptd0 + d1; \
|
|
|
|
continue; \
|
|
|
|
} else { \
|
2019-02-17 23:19:26 +00:00
|
|
|
if (IS_VISIT_MARKER) { \
|
|
|
|
\
|
|
|
|
continue; \
|
|
|
|
} \
|
2019-02-14 00:38:14 +00:00
|
|
|
PRIMI0; \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
derefa_body(d0, ptd0, var_in_term_unk, var_in_term_nvar)
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define WALK_COMPLEX_TERM() WALK_COMPLEX_TERM__({}, {}, {})
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define END_WALK() \
|
|
|
|
} \
|
2019-02-14 00:38:14 +00:00
|
|
|
} \
|
2019-02-11 09:28:46 +00:00
|
|
|
/* Do we still have compound terms to visit */ \
|
2019-02-14 00:38:14 +00:00
|
|
|
to_visit--; \
|
|
|
|
if (to_visit >= to_visit0) { \
|
|
|
|
pt0 = to_visit->pt0; \
|
|
|
|
pt0_end = to_visit->pt0_end; \
|
|
|
|
*to_visit->ptd0 = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
pop_text_stack(lvl);
|
2019-02-11 09:28:46 +00:00
|
|
|
|
|
|
|
#define def_aux_overflow() \
|
2019-02-14 00:38:14 +00:00
|
|
|
aux_overflow : { \
|
2019-02-21 21:03:44 +00:00
|
|
|
while (to_visit > to_visit0) { \
|
|
|
|
to_visit--; \
|
|
|
|
CELL *ptd0 = to_visit->ptd0; \
|
|
|
|
*ptd0 = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
clean_tr(TR0 PASS_REGS); \
|
2019-02-19 15:53:36 +00:00
|
|
|
auxsz += auxsz;\
|
2019-05-03 12:40:47 +01:00
|
|
|
pop_text_stack(lvl); \
|
|
|
|
goto reset; }
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define def_trail_overflow() \
|
2019-03-15 12:38:09 +00:00
|
|
|
trail_overflow: { \
|
2019-02-21 21:03:44 +00:00
|
|
|
while (to_visit > to_visit0) { \
|
|
|
|
to_visit--; \
|
|
|
|
CELL *ptd0 = to_visit->ptd0; \
|
|
|
|
*ptd0 = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
size_t expand = (TR - TR0) * sizeof(tr_fr_ptr *); \
|
2019-02-14 00:38:14 +00:00
|
|
|
clean_tr(TR0 PASS_REGS); \
|
|
|
|
HR = InitialH; \
|
|
|
|
pop_text_stack(lvl); \
|
2019-02-21 21:03:44 +00:00
|
|
|
/* Trail overflow */ \
|
|
|
|
if (!Yap_growtrail(expand, false)) { \
|
|
|
|
Yap_ThrowError(RESOURCE_ERROR_TRAIL, TermNil, expand);\
|
|
|
|
} \
|
|
|
|
goto reset;\
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define def_global_overflow() \
|
2019-02-14 00:38:14 +00:00
|
|
|
global_overflow : { \
|
|
|
|
while (to_visit > to_visit0) { \
|
|
|
|
to_visit--; \
|
|
|
|
CELL *ptd0 = to_visit->ptd0; \
|
|
|
|
*ptd0 = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
clean_tr(TR0 PASS_REGS); \
|
2019-05-03 12:40:47 +01:00
|
|
|
pop_text_stack(lvl); \
|
2019-02-14 00:38:14 +00:00
|
|
|
HR = InitialH; \
|
|
|
|
LOCAL_Error_TYPE = RESOURCE_ERROR_STACK; \
|
2019-02-21 21:03:44 +00:00
|
|
|
size_t expand = 0L; \
|
|
|
|
if (!Yap_gcl(expand, 3, ENV, gc_P(P, CP))) { \
|
|
|
|
Yap_ThrowError(RESOURCE_ERROR_STACK, TermNil, sizeof(CELL)*(HR-H0)); \
|
|
|
|
return false;\
|
|
|
|
}\
|
2019-05-03 12:40:47 +01:00
|
|
|
goto reset;\
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-13 09:44:24 +00:00
|
|
|
#define CYC_LIST \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (IS_VISIT_MARKER) { \
|
|
|
|
while (to_visit > to_visit0) { \
|
|
|
|
to_visit--; \
|
|
|
|
to_visit->ptd0[0] = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
pop_text_stack(lvl); \
|
|
|
|
return true; \
|
|
|
|
}
|
2019-02-04 01:08:18 +00:00
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
#define def_overflow() \
|
|
|
|
def_aux_overflow(); \
|
|
|
|
def_global_overflow(); \
|
2019-05-04 10:45:42 +01:00
|
|
|
def_trail_overflow()
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
|
2019-02-13 09:44:24 +00:00
|
|
|
#define CYC_APPL \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (IS_VISIT_MARKER) { \
|
|
|
|
while (to_visit > to_visit0) { \
|
|
|
|
to_visit--; \
|
|
|
|
to_visit->ptd0[0] = to_visit->d0; \
|
|
|
|
} \
|
|
|
|
pop_text_stack(lvl); \
|
|
|
|
return true; \
|
|
|
|
}
|
2019-02-04 01:08:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
@brief routine to locate all variables in a term, and its applications */
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
static Term cyclic_complex_term(CELL *pt0_, CELL *pt0_end_ USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
WALK_COMPLEX_TERM__(CYC_LIST, CYC_APPL, {});
|
|
|
|
/* leave an empty slot to fill in later */
|
|
|
|
END_WALK();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-04 01:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Yap_IsCyclicTerm(Term t USES_REGS) {
|
2019-02-17 23:19:26 +00:00
|
|
|
cs[2]++;
|
2019-02-04 01:08:18 +00:00
|
|
|
|
|
|
|
if (IsVarTerm(t)) {
|
2019-02-11 09:28:46 +00:00
|
|
|
return false;
|
2019-02-04 01:08:18 +00:00
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
2019-02-11 09:28:46 +00:00
|
|
|
return false;
|
2019-02-04 01:08:18 +00:00
|
|
|
} else {
|
2019-02-11 09:28:46 +00:00
|
|
|
return cyclic_complex_term(&(t)-1, &(t)PASS_REGS);
|
2019-02-04 01:08:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred cyclic_term( + _T_ )
|
|
|
|
|
|
|
|
|
|
|
|
Succeeds if the graph representation of the term has loops. Say,
|
|
|
|
the representation of a term `X` that obeys the equation `X=[X]`
|
|
|
|
term has a loop from the list to its head.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
static Int cyclic_term(USES_REGS1) /* cyclic_term(+T) */
|
|
|
|
{
|
|
|
|
return Yap_IsCyclicTerm(Deref(ARG1));
|
|
|
|
}
|
|
|
|
|
2019-02-17 23:19:26 +00:00
|
|
|
static Term BREAK_LOOP(CELL d0,struct non_single_struct_t *to_visit ) {
|
2019-02-09 09:43:26 +00:00
|
|
|
char buf[64];
|
2019-02-17 23:19:26 +00:00
|
|
|
snprintf(buf, 63, "@^[" Int_FORMAT "]", to_visit-(struct non_single_struct_t*)AtomOfTerm(d0));
|
2019-02-09 09:43:26 +00:00
|
|
|
return MkAtomTerm(Yap_LookupAtom(buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief routine to locate all variables in a term, and its applications */
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
static int cycles_in_complex_term( CELL *pt0_, CELL *pt0_end_ USES_REGS) {
|
2019-02-09 09:43:26 +00:00
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-02-19 15:53:36 +00:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
2019-05-03 12:40:47 +01:00
|
|
|
struct non_single_struct_t *to_visit0, *to_visit, *to_visit_max;
|
|
|
|
int lvl;
|
2019-05-04 10:45:42 +01:00
|
|
|
|
2019-05-03 12:40:47 +01:00
|
|
|
reset:
|
|
|
|
lvl = push_text_stack();
|
|
|
|
pt0 = pt0_, pt0_end = pt0_end_;
|
|
|
|
to_visit0 = Malloc(auxsz);
|
|
|
|
to_visit= to_visit0;
|
|
|
|
to_visit_max = to_visit0 + auxsz/sizeof(struct non_single_struct_t);
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-03-15 12:38:09 +00:00
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256) { \
|
|
|
|
/* Trail overflow */\
|
|
|
|
goto trail_overflow;\
|
|
|
|
}\
|
2019-02-27 04:23:21 +00:00
|
|
|
auxsz *= 2;
|
2019-02-11 09:28:46 +00:00
|
|
|
int rc = 0;
|
|
|
|
CELL *ptf;
|
2019-02-13 09:44:24 +00:00
|
|
|
ptf = HR;
|
|
|
|
HR++;
|
2019-02-11 09:28:46 +00:00
|
|
|
while (to_visit >= to_visit0) {
|
|
|
|
CELL d0;
|
|
|
|
CELL *ptd0;
|
2019-02-21 21:03:44 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
while (pt0 < pt0_end) {
|
|
|
|
++pt0;
|
|
|
|
ptd0 = pt0;
|
|
|
|
d0 = *ptd0;
|
2019-02-14 00:38:14 +00:00
|
|
|
list_loop:
|
2019-02-11 09:28:46 +00:00
|
|
|
deref_head(d0, var_in_term_unk);
|
2019-02-14 00:38:14 +00:00
|
|
|
var_in_term_nvar : {
|
|
|
|
if (IsPairTerm(d0)) {
|
|
|
|
if (to_visit + 32 >= to_visit_max) {
|
|
|
|
goto aux_overflow;
|
|
|
|
}
|
|
|
|
ptd0 = RepPair(d0);
|
|
|
|
d0 = ptd0[0];
|
|
|
|
if (IS_VISIT_MARKER) {
|
|
|
|
rc++;
|
2019-02-17 23:19:26 +00:00
|
|
|
*ptf++ = BREAK_LOOP(d0, to_visit);
|
2019-02-14 00:38:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*ptf++ = AbsPair(HR);
|
|
|
|
to_visit->pt0 = pt0;
|
|
|
|
to_visit->pt0_end = pt0_end;
|
|
|
|
to_visit->ptd0 = ptd0;
|
|
|
|
to_visit->d0 = d0;
|
|
|
|
to_visit->ptf = ptf;
|
|
|
|
to_visit++;
|
|
|
|
ptf = HR;
|
2019-03-15 12:38:09 +00:00
|
|
|
if (HR + 1024 > ASP) { \
|
|
|
|
goto global_overflow;\
|
|
|
|
}\
|
2019-02-14 00:38:14 +00:00
|
|
|
HR += 2;
|
|
|
|
*ptd0 = VISIT_MARKER;
|
|
|
|
pt0 = ptd0;
|
|
|
|
pt0_end = pt0+1;
|
|
|
|
ptf = HR - 2;
|
|
|
|
goto list_loop;
|
|
|
|
} else if (IsApplTerm(d0)) {
|
|
|
|
register Functor f;
|
2019-02-11 09:28:46 +00:00
|
|
|
/* store the terms to visit */
|
2019-02-14 00:38:14 +00:00
|
|
|
ptd0 = RepAppl(d0);
|
|
|
|
f = (Functor)(d0 = *ptd0);
|
|
|
|
if (IsExtensionFunctor(f)) {
|
2019-02-15 21:09:58 +00:00
|
|
|
*ptf++ = AbsAppl(ptd0);
|
2019-02-14 00:38:14 +00:00
|
|
|
continue;
|
2019-02-11 09:28:46 +00:00
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
if (IS_VISIT_MARKER) {
|
|
|
|
rc++;
|
2019-02-17 23:19:26 +00:00
|
|
|
*ptf++ = BREAK_LOOP(d0, to_visit);
|
2019-02-14 00:38:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (to_visit + 32 >= to_visit_max) {
|
|
|
|
goto aux_overflow;
|
|
|
|
}
|
|
|
|
*ptf++ = AbsAppl(HR);
|
|
|
|
to_visit->pt0 = pt0;
|
|
|
|
to_visit->pt0_end = pt0_end;
|
|
|
|
to_visit->ptd0 = ptd0;
|
|
|
|
to_visit->d0 = d0;
|
|
|
|
to_visit->ptf = ptf;
|
|
|
|
to_visit++;
|
|
|
|
|
|
|
|
*ptd0 = VISIT_MARKER;
|
|
|
|
*HR++ = (CELL)f;
|
|
|
|
ptf = HR;
|
|
|
|
Term d1 = ArityOfFunctor(f);
|
|
|
|
pt0 = ptd0;
|
|
|
|
pt0_end = ptd0 + (d1);
|
|
|
|
HR+=d1;
|
|
|
|
continue;
|
|
|
|
} else {
|
2019-02-17 23:19:26 +00:00
|
|
|
if (IS_VISIT_MARKER) {
|
|
|
|
rc++;
|
|
|
|
*ptf++ = BREAK_LOOP(d0, to_visit);
|
|
|
|
continue;
|
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
*ptf++ = d0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
derefa_body(d0, ptd0, var_in_term_unk, var_in_term_nvar);
|
|
|
|
*ptf++ = d0;
|
|
|
|
}
|
|
|
|
}
|
2019-02-11 09:28:46 +00:00
|
|
|
/* Do we still have compound terms to visit */
|
2019-02-14 00:38:14 +00:00
|
|
|
to_visit--;
|
|
|
|
if (to_visit >= to_visit0) {
|
|
|
|
pt0 = to_visit->pt0;
|
|
|
|
pt0_end = to_visit->pt0_end;
|
|
|
|
ptf = to_visit->ptf;
|
|
|
|
*to_visit->ptd0 = to_visit->d0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pop_text_stack(lvl);
|
2019-02-09 09:43:26 +00:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
return rc;
|
2019-02-09 09:43:26 +00:00
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-21 21:03:44 +00:00
|
|
|
|
2019-05-04 10:45:42 +01:00
|
|
|
|
2019-02-09 09:43:26 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Term Yap_CyclesInTerm(Term t USES_REGS) {
|
2019-02-17 23:19:26 +00:00
|
|
|
cs[3]++;
|
|
|
|
t = Deref(t);
|
2019-02-09 09:43:26 +00:00
|
|
|
if (IsVarTerm(t)) {
|
2019-02-10 00:18:08 +00:00
|
|
|
return t;
|
2019-02-09 09:43:26 +00:00
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
2019-02-10 00:18:08 +00:00
|
|
|
return t;
|
2019-02-09 09:43:26 +00:00
|
|
|
} else {
|
2019-02-11 09:28:46 +00:00
|
|
|
CELL *Hi = HR;
|
|
|
|
if ( cycles_in_complex_term(&(t)-1, &(t)PASS_REGS) >0) {
|
2019-02-11 18:10:31 +00:00
|
|
|
return Hi[0];
|
2019-02-11 09:28:46 +00:00
|
|
|
} else {
|
|
|
|
HR = Hi;
|
|
|
|
return t;
|
|
|
|
}
|
2019-02-09 09:43:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred cycles_in_term( + _T_ )
|
|
|
|
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Succeeds if the graph representation of the term has markers in every
|
|
|
|
loop. Say, the representation of a term `X` that obeys the equation `X=[X]`
|
2019-02-09 09:43:26 +00:00
|
|
|
term has a loop from the list to its head.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
static Int cycles_in_term(USES_REGS1) /* cyclic_term(+T) */
|
|
|
|
{
|
|
|
|
return Yap_CyclesInTerm(Deref(ARG1));
|
|
|
|
}
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
/**
|
|
|
|
@brief routine to locate all variables in a term, and its applications */
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
static bool ground_complex_term(CELL * pt0_, CELL * pt0_end_ USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-02-04 01:08:18 +00:00
|
|
|
|
|
|
|
WALK_COMPLEX_TERM();
|
|
|
|
/* leave an empty slot to fill in later */
|
|
|
|
while (to_visit > to_visit0) {
|
|
|
|
to_visit--;
|
|
|
|
|
|
|
|
CELL *ptd0 = to_visit->ptd0;
|
|
|
|
*ptd0 = to_visit->d0;
|
|
|
|
}
|
|
|
|
pop_text_stack(lvl);
|
|
|
|
return false;
|
|
|
|
|
|
|
|
END_WALK();
|
|
|
|
/* Do we still have compound terms to visit */
|
|
|
|
|
|
|
|
pop_text_stack(lvl);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-04 01:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Yap_IsGroundTerm(Term t) {
|
|
|
|
CACHE_REGS
|
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
return false;
|
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return ground_complex_term(&(t)-1, &(t)PASS_REGS);
|
|
|
|
}
|
2019-02-04 01:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred ground( _T_) is iso
|
|
|
|
|
|
|
|
|
|
|
|
Succeeds if there are no free variables in the term _T_.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
static Int ground(USES_REGS1) /* ground(+T) */
|
|
|
|
{
|
|
|
|
return Yap_IsGroundTerm(Deref(ARG1));
|
|
|
|
}
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
static Int var_in_complex_term(CELL *pt0_, CELL *pt0_end_ ,
|
2019-02-14 00:38:14 +00:00
|
|
|
Term v USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
WALK_COMPLEX_TERM();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-06 15:08:25 +00:00
|
|
|
if ((CELL)ptd0 == v) { /* we found it */
|
2019-02-11 09:28:46 +00:00
|
|
|
/* Do we still have compound terms to visit */
|
2019-02-14 00:38:14 +00:00
|
|
|
while (to_visit > to_visit0) {
|
2019-02-01 13:14:33 +00:00
|
|
|
to_visit--;
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
CELL *ptd0 = to_visit->ptd0;
|
|
|
|
*ptd0 = to_visit->d0;
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
pop_text_stack(lvl);
|
2019-02-14 00:38:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
goto restart;
|
|
|
|
END_WALK();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
if (to_visit > to_visit0) {
|
|
|
|
to_visit--;
|
|
|
|
|
|
|
|
CELL *ptd0 = to_visit->ptd0;
|
|
|
|
*ptd0 = to_visit->d0;
|
|
|
|
pt0 = to_visit->pt0;
|
|
|
|
pt0_end = to_visit->pt0_end;
|
|
|
|
}
|
|
|
|
pop_text_stack(lvl);
|
|
|
|
return false;
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
static Int var_in_term(
|
|
|
|
Term v, Term t USES_REGS) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
2019-02-01 13:14:33 +00:00
|
|
|
must_be_variable(v);
|
|
|
|
t = Deref(t);
|
2019-01-30 15:24:06 +00:00
|
|
|
if (IsVarTerm(t)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
return (v == t);
|
2019-01-30 15:24:06 +00:00
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
return (false);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
return (var_in_complex_term(&(t)-1, &(t), v PASS_REGS));
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
/** @pred variable_in_term(? _Term_,? _Var_)
|
|
|
|
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Succeed if the second argument _Var_ is a variable and occurs in
|
|
|
|
term _Term_.
|
2019-02-01 13:14:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
static Int variable_in_term(USES_REGS1) {
|
|
|
|
return var_in_term(Deref(ARG2), Deref(ARG1) PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-02-04 01:08:18 +00:00
|
|
|
* @brief routine to locate all variables in a term, and its applications.
|
|
|
|
*/
|
2019-02-21 21:03:44 +00:00
|
|
|
static Term vars_in_complex_term(CELL *pt0_, CELL *pt0_end_ ,
|
2019-02-14 00:38:14 +00:00
|
|
|
Term inp USES_REGS) {
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
Int count=0;
|
|
|
|
while (!IsVarTerm(inp) && IsPairTerm(inp)) {
|
|
|
|
Term t = HeadOfTerm(inp);
|
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
CELL *ptr = VarOfTerm(t);
|
|
|
|
*ptr = TermFoundVar;
|
|
|
|
TrailTerm(TR++) = t;
|
|
|
|
count++;
|
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256) {
|
|
|
|
clean_tr(TR - count PASS_REGS);
|
|
|
|
if (!Yap_growtrail(count * sizeof(tr_fr_ptr *), false)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inp = TailOfTerm(inp);
|
|
|
|
}
|
2019-05-04 10:45:42 +01:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
CELL output = AbsPair(HR);
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
|
|
|
|
|
|
|
WALK_COMPLEX_TERM();
|
2019-02-01 13:14:33 +00:00
|
|
|
/* do or pt2 are unbound */
|
2019-02-21 20:19:31 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
if (HR + 1024 > ASP) {
|
|
|
|
goto global_overflow;
|
|
|
|
}
|
|
|
|
HR[1] = AbsPair(HR + 2);
|
|
|
|
HR += 2;
|
|
|
|
HR[-2] = (CELL)ptd0;
|
|
|
|
/* next make sure noone will see this as a variable again */
|
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256) {
|
|
|
|
/* Trail overflow */
|
|
|
|
goto trail_overflow;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
TrailTerm(TR++) = (CELL)ptd0;
|
2019-02-21 20:19:31 +00:00
|
|
|
*ptd0 = TermFoundVar;
|
2019-02-06 15:08:25 +00:00
|
|
|
END_WALK();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
clean_tr(TR0-count PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
pop_text_stack(lvl);
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
if (HR != InitialH) {
|
|
|
|
/* close the list */
|
|
|
|
Term t2 = Deref(inp);
|
|
|
|
if (IsVarTerm(t2)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
RESET_VARIABLE(HR - 1);
|
2019-02-21 20:19:31 +00:00
|
|
|
Yap_unify((CELL)(HR - 1), t2);
|
2019-01-30 15:24:06 +00:00
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
HR[-1] = t2; /* don't need to trail */
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
return (output);
|
2019-01-30 15:24:06 +00:00
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
return (inp);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
/**
|
2019-02-11 09:28:46 +00:00
|
|
|
* @pred variables_in_term( +_T_, +_SetOfVariables_, +_ExtendedSetOfVariables_
|
|
|
|
* )
|
2019-02-04 01:08:18 +00:00
|
|
|
*
|
|
|
|
* _SetOfVariables_ must be a list of unbound variables. If so,
|
|
|
|
* _ExtendedSetOfVariables_ will include all te variables in the union
|
|
|
|
* of `vars(_T_)` and _SetOfVariables_.
|
|
|
|
*/
|
2019-02-11 09:28:46 +00:00
|
|
|
static Int variables_in_term(
|
|
|
|
USES_REGS1) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out, inp;
|
|
|
|
|
|
|
|
inp = Deref(ARG2);
|
2019-02-14 00:38:14 +00:00
|
|
|
Term t = Deref(ARG1);
|
2019-02-19 15:53:36 +00:00
|
|
|
out = vars_in_complex_term(&(t)-1, &(t), inp PASS_REGS);
|
2019-02-14 00:38:14 +00:00
|
|
|
return Yap_unify(ARG3, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred term_variables(? _Term_, - _Variables_, +_ExternalVars_) is iso
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unify the difference list between _Variables_ and _ExternaVars_
|
|
|
|
with the list of all variables of term _Term_. The variables
|
|
|
|
occur in the order of their first appearance when traversing the
|
|
|
|
term depth-first, left-to-right.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2019-02-17 23:19:26 +00:00
|
|
|
static Int term_variables3(
|
2019-02-11 09:28:46 +00:00
|
|
|
USES_REGS1) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
2019-02-17 23:19:26 +00:00
|
|
|
cs[0]++;
|
2019-01-30 15:24:06 +00:00
|
|
|
Term t = Deref(ARG1);
|
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
Term out = Yap_MkNewPairTerm();
|
2019-02-01 13:14:33 +00:00
|
|
|
return Yap_unify(t, HeadOfTerm(out)) &&
|
2019-02-14 00:38:14 +00:00
|
|
|
Yap_unify(ARG3, TailOfTerm(out)) && Yap_unify(out, ARG2);
|
2019-02-01 13:14:33 +00:00
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
2019-01-30 15:24:06 +00:00
|
|
|
return Yap_unify(ARG2, ARG3);
|
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
out = vars_in_complex_term(&(t)-1, &(t), ARG3 PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
return Yap_unify(ARG2, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exports a nil-terminated list with all the variables in a term.
|
|
|
|
* @param[t] the term
|
2019-02-11 09:28:46 +00:00
|
|
|
* @param[arity] the arity of the calling predicate (required for exact
|
|
|
|
* garbage collection).
|
2019-01-30 15:24:06 +00:00
|
|
|
* @param[USES_REGS] threading
|
|
|
|
*/
|
2019-02-01 13:14:33 +00:00
|
|
|
Term Yap_TermVariables(
|
2019-02-11 09:28:46 +00:00
|
|
|
Term t, UInt arity USES_REGS) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
t = Deref(t);
|
2019-01-30 15:24:06 +00:00
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
return MkPairTerm(t, TermNil);
|
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
|
|
|
return TermNil;
|
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
out = vars_in_complex_term(&(t)-1, &(t), TermNil PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
return out;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 20:19:31 +00:00
|
|
|
static Term Yap_TermAddVariables(
|
|
|
|
Term t, Term vs USES_REGS) /* variables in term t */
|
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
|
|
|
|
t = Deref(t);
|
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
return MkPairTerm(t, TermNil);
|
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
|
|
|
return TermNil;
|
|
|
|
} else {
|
|
|
|
out = vars_in_complex_term(&(t)-1, &(t), vs PASS_REGS);
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
/** @pred term_variables(? _Term_, - _Variables_) is iso
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unify _Variables_ with the list of all variables of term
|
|
|
|
_Term_. The variables occur in the order of their first
|
|
|
|
appearance when traversing the term depth-first, left-to-right.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2019-02-17 23:19:26 +00:00
|
|
|
static Int term_variables(
|
2019-02-11 09:28:46 +00:00
|
|
|
USES_REGS1) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
if (!Yap_IsListOrPartialListTerm(ARG2)) {
|
2019-02-21 21:03:44 +00:00
|
|
|
Yap_ThrowError(TYPE_ERROR_LIST, ARG2, "term_variables/2");
|
2019-02-01 13:14:33 +00:00
|
|
|
return false;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Term t = Deref(ARG1);
|
2019-02-01 13:14:33 +00:00
|
|
|
|
|
|
|
out = vars_in_complex_term(&(t)-1, &(t), TermNil PASS_REGS);
|
2019-02-21 21:03:44 +00:00
|
|
|
return Yap_unify(ARG2, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** routine to locate attributed variables */
|
|
|
|
|
|
|
|
typedef struct att_rec {
|
|
|
|
CELL *beg, *end;
|
|
|
|
CELL oval;
|
|
|
|
} att_rec_t;
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
static Term attvars_in_complex_term(
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL *pt0_, CELL *pt0_end_ , Term inp USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL output = inp;
|
2019-02-01 13:14:33 +00:00
|
|
|
WALK_COMPLEX_TERM();
|
|
|
|
if (IsAttVar(ptd0)) {
|
|
|
|
/* do or pt2 are unbound */
|
|
|
|
attvar_record *a0 = RepAttVar(ptd0);
|
2019-02-21 21:03:44 +00:00
|
|
|
d0 = *ptd0;
|
2019-02-01 13:14:33 +00:00
|
|
|
/* leave an empty slot to fill in later */
|
|
|
|
if (HR + 1024 > ASP) {
|
|
|
|
goto global_overflow;
|
|
|
|
}
|
2019-02-11 09:28:46 +00:00
|
|
|
output = MkPairTerm((CELL) & (a0->Done), output);
|
2019-02-01 13:14:33 +00:00
|
|
|
/* store the terms to visit */
|
|
|
|
if (to_visit + 32 >= to_visit_max) {
|
|
|
|
goto aux_overflow;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-21 21:03:44 +00:00
|
|
|
TrailTerm(TR++) = a0->Done;
|
|
|
|
a0->Done=TermNil;
|
|
|
|
if ((tr_fr_ptr)LOCAL_TrailTop - TR < 1024) {
|
|
|
|
|
|
|
|
if (!Yap_growtrail((TR - TR0) * sizeof(tr_fr_ptr *), true)) {
|
|
|
|
goto trail_overflow;
|
|
|
|
}
|
|
|
|
pop_text_stack(lvl);
|
|
|
|
}
|
|
|
|
|
|
|
|
pt0_end = &a0->Atts;
|
2019-02-01 13:14:33 +00:00
|
|
|
pt0 = pt0_end - 1;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-06 15:08:25 +00:00
|
|
|
END_WALK();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
clean_tr(TR0 PASS_REGS);
|
|
|
|
pop_text_stack(lvl);
|
2019-02-17 23:19:26 +00:00
|
|
|
/*fprintf(stderr,"<%ld at %s\n", d0, __FUNCTION__)*/;
|
2019-02-21 21:03:44 +00:00
|
|
|
return output;
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
/** @pred term_attvars(+ _Term_,- _AttVars_)
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
_AttVars_ is a list of all attributed variables in _Term_ and
|
|
|
|
its attributes. I.e., term_attvars/2 works recursively through
|
|
|
|
attributes. This predicate is Cycle-safe.
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
*/
|
2019-02-21 21:03:44 +00:00
|
|
|
static Int term_attvars(USES_REGS1) /* variables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
|
|
|
|
Term t = Deref(ARG1);
|
|
|
|
if (IsPrimitiveTerm(t)) {
|
|
|
|
return Yap_unify(TermNil, ARG2);
|
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
out = attvars_in_complex_term(&(t)-1, &(t), TermNil PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-21 21:03:44 +00:00
|
|
|
return Yap_unify(ARG2, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
/** @brief output the difference between variables in _T_ and variables in
|
|
|
|
* some list.
|
2019-01-30 15:24:06 +00:00
|
|
|
*/
|
2019-02-11 09:28:46 +00:00
|
|
|
static Term new_vars_in_complex_term(
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL *pt0_, CELL *pt0_end_ , Term inp USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-02-19 15:53:36 +00:00
|
|
|
Int n=0;
|
2019-02-05 10:31:17 +00:00
|
|
|
CELL output = TermNil;
|
2019-05-03 12:40:47 +01:00
|
|
|
Term inp0 = inp;
|
|
|
|
|
2019-02-27 04:23:21 +00:00
|
|
|
while (!IsVarTerm(inp) && IsPairTerm(inp)) {
|
2019-02-04 01:08:18 +00:00
|
|
|
Term t = HeadOfTerm(inp);
|
|
|
|
if (IsVarTerm(t)) {
|
2019-02-19 15:53:36 +00:00
|
|
|
n++;
|
2019-05-03 12:40:47 +01:00
|
|
|
TrailTerm(TR++) = t;
|
2019-05-04 10:45:42 +01:00
|
|
|
TrailTerm(TR++) = t;
|
2019-05-03 12:40:47 +01:00
|
|
|
*VarOfTerm(t) = TermFoundVar;
|
|
|
|
if ((tr_fr_ptr)LOCAL_TrailTop - TR < 1024) {
|
|
|
|
size_t expand = (tr_fr_ptr)LOCAL_TrailTop - TR;
|
|
|
|
clean_tr(TR0 PASS_REGS);
|
|
|
|
*HR++ = inp0;
|
2019-05-04 10:45:42 +01:00
|
|
|
/* Trail overflow */
|
|
|
|
if (!Yap_growtrail(expand, false)) {
|
2019-05-03 12:40:47 +01:00
|
|
|
Yap_ThrowError(RESOURCE_ERROR_TRAIL, TermNil, expand);
|
|
|
|
}
|
|
|
|
inp = *--HR;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
inp = TailOfTerm(inp);
|
|
|
|
}
|
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
WALK_COMPLEX_TERM();
|
|
|
|
output = MkPairTerm((CELL)ptd0, output);
|
2019-03-15 12:38:09 +00:00
|
|
|
TrailTerm(TR++) = *ptd0;
|
|
|
|
*ptd0 = TermFoundVar;
|
2019-02-14 00:38:14 +00:00
|
|
|
if ((tr_fr_ptr)LOCAL_TrailTop - TR < 1024) {
|
|
|
|
goto trail_overflow;
|
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
/* leave an empty slot to fill in later */
|
2019-02-14 00:38:14 +00:00
|
|
|
if (HR + 1024 > ASP) {
|
|
|
|
goto global_overflow;
|
|
|
|
}
|
|
|
|
END_WALK();
|
2019-02-11 09:28:46 +00:00
|
|
|
|
2019-05-04 10:45:42 +01:00
|
|
|
clean_tr(TR0 PASS_REGS);
|
2019-02-14 00:38:14 +00:00
|
|
|
pop_text_stack(lvl);
|
2019-02-21 20:19:31 +00:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
return output;
|
2019-02-11 09:28:46 +00:00
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred new_variables_in_term(+_CurrentVariables_, ? _Term_, -_Variables_)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unify _Variables_ with the list of all variables of term
|
2019-02-11 09:28:46 +00:00
|
|
|
_Term_ that do not occur in _CurrentVariables_. The variables occur in
|
|
|
|
the order of their first appearance when traversing the term depth-first,
|
|
|
|
left-to-right.
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
*/
|
2019-02-11 09:28:46 +00:00
|
|
|
static Int p_new_variables_in_term(
|
|
|
|
USES_REGS1) /* variables within term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
|
|
|
|
Term t = Deref(ARG2);
|
2019-02-01 13:14:33 +00:00
|
|
|
if (IsPrimitiveTerm(t))
|
2019-01-30 15:24:06 +00:00
|
|
|
out = TermNil;
|
2019-02-01 13:14:33 +00:00
|
|
|
else {
|
|
|
|
out = new_vars_in_complex_term(&(t)-1, &(t), Deref(ARG1) PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-21 21:03:44 +00:00
|
|
|
return Yap_unify(ARG3, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define FOUND_VAR() \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (d0 == TermFoundVar) { \
|
2019-02-11 09:28:46 +00:00
|
|
|
/* leave an empty slot to fill in later */ \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (HR + 1024 > ASP) { \
|
|
|
|
goto global_overflow; \
|
|
|
|
} \
|
|
|
|
HR[1] = AbsPair(HR + 2); \
|
|
|
|
HR += 2; \
|
|
|
|
HR[-2] = (CELL)ptd0; \
|
|
|
|
*ptd0 = TermNil; \
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
static Term vars_within_complex_term(
|
2019-02-21 21:03:44 +00:00
|
|
|
CELL *pt0_, CELL *pt0_end_, Term inp USES_REGS) {
|
2019-02-19 15:53:36 +00:00
|
|
|
Int n=0;
|
2019-01-30 15:24:06 +00:00
|
|
|
CELL output = AbsPair(HR);
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-05-04 10:45:42 +01:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
while (!IsVarTerm(inp) && IsPairTerm(inp)) {
|
|
|
|
Term t = HeadOfTerm(inp);
|
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
CELL *ptr = VarOfTerm(t);
|
|
|
|
*ptr = TermFoundVar;
|
2019-02-19 15:53:36 +00:00
|
|
|
n++;
|
2019-01-30 15:24:06 +00:00
|
|
|
TrailTerm(TR++) = t;
|
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256) {
|
2019-02-19 15:53:36 +00:00
|
|
|
Yap_growtrail(2*n * sizeof(tr_fr_ptr *), true);
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
inp = TailOfTerm(inp);
|
|
|
|
}
|
|
|
|
|
|
|
|
WALK_COMPLEX_TERM__({}, {}, FOUND_VAR());
|
|
|
|
goto restart;
|
|
|
|
END_WALK();
|
|
|
|
|
2019-05-04 10:45:42 +01:00
|
|
|
clean_tr(TR0 PASS_REGS);
|
2019-02-14 00:38:14 +00:00
|
|
|
pop_text_stack(lvl);
|
|
|
|
if (HR != InitialH) {
|
|
|
|
HR[-1] = TermNil;
|
|
|
|
return output;
|
|
|
|
} else {
|
|
|
|
return TermNil;
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @pred variables_within_term(+_CurrentVariables_, ? _Term_, -_Variables_)
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
Unify _Variables_ with the list of all variables of term _Term_
|
|
|
|
that *also* occur in _CurrentVariables_. The variables occur in
|
|
|
|
the order of their first appearance when traversing the term
|
|
|
|
depth-first, left-to-right.
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
This predicate performs the opposite of new_variables_in_term/3.
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
*/
|
2019-02-01 13:14:33 +00:00
|
|
|
static Int p_variables_within_term(USES_REGS1) /* variables within term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
|
|
|
|
Term t = Deref(ARG2);
|
2019-02-01 13:14:33 +00:00
|
|
|
if (IsPrimitiveTerm(t))
|
2019-01-30 15:24:06 +00:00
|
|
|
out = TermNil;
|
2019-02-01 13:14:33 +00:00
|
|
|
else {
|
|
|
|
out = vars_within_complex_term(&(t)-1, &(t), Deref(ARG1) PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-21 21:03:44 +00:00
|
|
|
return Yap_unify(ARG3, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
/* variables within term t */
|
2019-02-21 20:19:31 +00:00
|
|
|
static Int free_variables_in_term(
|
2019-02-19 15:53:36 +00:00
|
|
|
USES_REGS1)
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term out;
|
|
|
|
Term t, t0;
|
|
|
|
Term found_module = 0L;
|
2019-02-27 04:23:21 +00:00
|
|
|
Term bounds = TermNil;
|
2019-02-21 20:19:31 +00:00
|
|
|
|
|
|
|
t = t0 = Deref(ARG1);
|
2019-03-15 12:38:09 +00:00
|
|
|
|
2019-02-21 20:19:31 +00:00
|
|
|
while (!IsVarTerm(t) && IsApplTerm(t)) {
|
|
|
|
Functor f = FunctorOfTerm(t);
|
|
|
|
if (f == FunctorHat) {
|
2019-02-27 04:23:21 +00:00
|
|
|
bounds = MkPairTerm(ArgOfTerm(1,t),bounds);
|
2019-02-21 20:19:31 +00:00
|
|
|
} else if (f == FunctorModule) {
|
|
|
|
found_module = ArgOfTerm(1, t);
|
|
|
|
} else if (f == FunctorCall) {
|
|
|
|
t = ArgOfTerm(1, t);
|
|
|
|
} else if (f == FunctorExecuteInMod) {
|
|
|
|
found_module = ArgOfTerm(2, t);
|
|
|
|
t = ArgOfTerm(1, t);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
t = ArgOfTerm(2, t);
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
|
|
|
if (IsPrimitiveTerm(t))
|
|
|
|
out = TermNil;
|
|
|
|
else {
|
2019-02-27 04:23:21 +00:00
|
|
|
out = new_vars_in_complex_term(&(t)-1, &(t), Yap_TermVariables(bounds, 3) PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-05-04 10:45:42 +01:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
if (found_module && t != t0) {
|
|
|
|
Term ts[2];
|
|
|
|
ts[0] = found_module;
|
|
|
|
ts[1] = t;
|
|
|
|
t = Yap_MkApplTerm(FunctorModule, 2, ts);
|
|
|
|
}
|
|
|
|
return Yap_unify(ARG2, t) && Yap_unify(ARG3, out);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-20 10:45:21 +00:00
|
|
|
#define FOUND_VAR_AGAIN() \
|
|
|
|
if (d0 == TermFoundVar) \
|
|
|
|
{ \
|
|
|
|
HR[0] = (CELL)ptd0; \
|
|
|
|
HR[1] = AbsPair(HR + 2); \
|
|
|
|
HR += 2; \
|
|
|
|
*ptd0 = TermRefoundVar; \
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
static Term non_singletons_in_complex_term(CELL * pt0_,
|
|
|
|
CELL * pt0_end_ USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
WALK_COMPLEX_TERM__({}, {}, FOUND_VAR_AGAIN());
|
|
|
|
/* do or pt2 are unbound */
|
2019-02-20 10:45:21 +00:00
|
|
|
*ptd0 = TermFoundVar;
|
|
|
|
/* next make sure noone will see this as a variable again */
|
|
|
|
if (TR > (tr_fr_ptr)LOCAL_TrailTop - 256)
|
|
|
|
{
|
|
|
|
goto trail_overflow;
|
|
|
|
}
|
|
|
|
TrailTerm(TR++) = (CELL)ptd0;
|
2019-02-01 13:14:33 +00:00
|
|
|
END_WALK();
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
clean_tr(TR0 PASS_REGS);
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
pop_text_stack(lvl);
|
|
|
|
if (HR != InitialH) {
|
|
|
|
/* close the list */
|
|
|
|
HR[-1] = Deref(ARG2);
|
2019-02-20 10:45:21 +00:00
|
|
|
return AbsPair(InitialH);
|
2019-01-30 15:24:06 +00:00
|
|
|
} else {
|
|
|
|
return ARG2;
|
|
|
|
}
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
static Int p_non_singletons_in_term(
|
2019-02-11 09:28:46 +00:00
|
|
|
USES_REGS1) /* non_singletons in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
Term t;
|
|
|
|
Term out;
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
t = Deref(ARG1);
|
2019-01-30 15:24:06 +00:00
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
out = ARG2;
|
2019-02-01 13:14:33 +00:00
|
|
|
} else if (IsPrimitiveTerm(t)) {
|
2019-01-30 15:24:06 +00:00
|
|
|
out = ARG2;
|
|
|
|
} else {
|
2019-02-01 13:14:33 +00:00
|
|
|
out = non_singletons_in_complex_term(&(t)-1, &(t)PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-20 10:45:21 +00:00
|
|
|
return Yap_unify(ARG3,out);
|
2019-02-27 04:22:59 +00:00
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-08 15:01:12 +00:00
|
|
|
static Term numbervar(Int me USES_REGS) {
|
2019-01-30 15:24:06 +00:00
|
|
|
Term ts[1];
|
2019-02-08 15:01:12 +00:00
|
|
|
ts[0] = MkIntegerTerm(me);
|
2019-01-30 15:24:06 +00:00
|
|
|
return Yap_MkApplTerm(FunctorDollarVar, 1, ts);
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
static Term numbervar_singleton(USES_REGS1) {
|
2019-01-30 15:24:06 +00:00
|
|
|
Term ts[1];
|
|
|
|
ts[0] = MkIntegerTerm(-1);
|
|
|
|
return Yap_MkApplTerm(FunctorDollarVar, 1, ts);
|
|
|
|
}
|
|
|
|
|
2019-02-08 15:01:12 +00:00
|
|
|
static void renumbervar(Term t, Int me USES_REGS) {
|
2019-01-30 15:24:06 +00:00
|
|
|
Term *ts = RepAppl(t);
|
2019-02-08 15:01:12 +00:00
|
|
|
ts[1] = MkIntegerTerm(me);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define RENUMBER_SINGLES \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (singles) { \
|
|
|
|
renumbervar(d0, numbv++ PASS_REGS); \
|
|
|
|
goto restart; \
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
static Int numbervars_in_complex_term(CELL * pt0_, CELL * pt0_end_, Int numbv,
|
2019-02-14 00:38:14 +00:00
|
|
|
int singles USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-05 10:31:17 +00:00
|
|
|
WALK_COMPLEX_TERM__({}, {}, {});
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-04 22:10:30 +00:00
|
|
|
if (IsAttVar(pt0))
|
2019-02-11 09:28:46 +00:00
|
|
|
continue;
|
2019-02-01 13:14:33 +00:00
|
|
|
/* do or pt2 are unbound */
|
2019-02-15 21:09:58 +00:00
|
|
|
if (singles)
|
2019-02-04 22:10:30 +00:00
|
|
|
d0 = numbervar_singleton(PASS_REGS1);
|
2019-02-01 13:14:33 +00:00
|
|
|
else
|
2019-02-04 22:10:30 +00:00
|
|
|
d0 = numbervar(numbv++ PASS_REGS);
|
2019-02-01 13:14:33 +00:00
|
|
|
/* leave an empty slot to fill in later */
|
|
|
|
if (HR + 1024 > ASP) {
|
|
|
|
goto global_overflow;
|
|
|
|
}
|
|
|
|
/* next make sure noone will see this as a variable again */
|
2019-02-04 22:10:30 +00:00
|
|
|
YapBind(ptd0, d0);
|
2019-02-01 13:14:33 +00:00
|
|
|
|
|
|
|
END_WALK();
|
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
pop_text_stack(lvl);
|
|
|
|
return numbv;
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-01 13:14:33 +00:00
|
|
|
|
|
|
|
}
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
Int Yap_NumberVars(Term inp, Int numbv,
|
2019-02-11 09:28:46 +00:00
|
|
|
bool handle_singles) /*
|
|
|
|
* numbervariables in term t */
|
2019-01-30 15:24:06 +00:00
|
|
|
{
|
|
|
|
CACHE_REGS
|
2019-02-14 00:38:14 +00:00
|
|
|
Int out;
|
2019-01-30 15:24:06 +00:00
|
|
|
Term t;
|
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
t = Deref(inp);
|
2019-02-01 13:14:33 +00:00
|
|
|
if (IsPrimitiveTerm(t)) {
|
2019-01-30 15:24:06 +00:00
|
|
|
return numbv;
|
|
|
|
} else {
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
out = numbervars_in_complex_term(&(t)-1, &(t), numbv,
|
2019-02-14 00:38:14 +00:00
|
|
|
handle_singles PASS_REGS);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-21 21:03:44 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
return out;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
/** @pred numbervars( _T_,+ _N1_,- _Nn_)
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
Instantiates each variable in term _T_ to a term of the form:
|
|
|
|
`$VAR( _I_)`, with _I_ increasing from _N1_ to _Nn_.
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
*/
|
|
|
|
static Int p_numbervars(USES_REGS1) {
|
2019-01-30 15:24:06 +00:00
|
|
|
Term t2 = Deref(ARG2);
|
|
|
|
Int out;
|
|
|
|
|
|
|
|
if (IsVarTerm(t2)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
Yap_Error(INSTANTIATION_ERROR, t2, "numbervars/3");
|
|
|
|
return false;
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
|
|
|
if (!IsIntegerTerm(t2)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
Yap_Error(TYPE_ERROR_INTEGER, t2, "numbervars/3");
|
|
|
|
return (false);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
if ((out = Yap_NumberVars(ARG1, IntegerOfTerm(t2), false)) < 0)
|
|
|
|
return false;
|
2019-01-30 15:24:06 +00:00
|
|
|
return Yap_unify(ARG3, MkIntegerTerm(out));
|
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
#define MAX_NUMBERED \
|
2019-02-14 00:38:14 +00:00
|
|
|
if (FunctorOfTerm(d0) == FunctorDollarVar) { \
|
|
|
|
Term t1 = ArgOfTerm(1, d0); \
|
|
|
|
Int i; \
|
|
|
|
if (IsIntegerTerm(t1) && ((i = IntegerOfTerm(t1)) > *maxp)) \
|
|
|
|
*maxp = i; \
|
|
|
|
goto restart; \
|
|
|
|
}
|
2019-01-31 16:40:41 +00:00
|
|
|
|
2019-02-19 15:53:36 +00:00
|
|
|
static int max_numbered_var(CELL * pt0_, CELL * pt0_end_,
|
2019-02-14 00:38:14 +00:00
|
|
|
Int * maxp USES_REGS) {
|
2019-05-03 12:40:47 +01:00
|
|
|
CELL *pt0, *pt0_end;
|
2019-05-04 10:45:42 +01:00
|
|
|
int lvl;
|
2019-05-03 12:40:47 +01:00
|
|
|
size_t auxsz = 1024 * sizeof(struct non_single_struct_t);
|
|
|
|
struct non_single_struct_t *to_visit0, *to_visit,* to_visit_max;
|
|
|
|
CELL *InitialH = HR;
|
|
|
|
tr_fr_ptr TR0 = TR;
|
2019-02-21 21:03:44 +00:00
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
WALK_COMPLEX_TERM__({}, MAX_NUMBERED, {});
|
|
|
|
END_WALK();
|
2019-01-31 16:40:41 +00:00
|
|
|
/* Do we still have compound terms to visit */
|
|
|
|
if (to_visit > to_visit0) {
|
|
|
|
to_visit--;
|
|
|
|
|
|
|
|
pt0 = to_visit->pt0;
|
|
|
|
pt0_end = to_visit->pt0_end;
|
|
|
|
CELL *ptd0 = to_visit->ptd0;
|
|
|
|
*ptd0 = to_visit->d0;
|
|
|
|
}
|
|
|
|
|
|
|
|
prune(B PASS_REGS);
|
|
|
|
pop_text_stack(lvl);
|
|
|
|
return 0;
|
|
|
|
|
2019-03-15 12:38:09 +00:00
|
|
|
def_overflow();
|
2019-02-01 13:14:33 +00:00
|
|
|
}
|
2019-01-31 16:40:41 +00:00
|
|
|
|
2019-02-02 22:33:18 +00:00
|
|
|
static Int MaxNumberedVar(Term inp, UInt arity PASS_REGS) {
|
2019-01-31 16:40:41 +00:00
|
|
|
Term t = Deref(inp);
|
|
|
|
|
|
|
|
if (IsPrimitiveTerm(t)) {
|
|
|
|
return MkIntegerTerm(0);
|
|
|
|
} else {
|
|
|
|
Int res;
|
|
|
|
Int max;
|
2019-02-01 13:14:33 +00:00
|
|
|
res = max_numbered_var(&t - 1, &t, &max PASS_REGS) - 1;
|
|
|
|
if (res < 0)
|
|
|
|
return -1;
|
2019-01-31 16:40:41 +00:00
|
|
|
return MkIntegerTerm(max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-02 22:33:18 +00:00
|
|
|
/**
|
|
|
|
* @pred largest_numbervar( +_Term_, -Max)
|
|
|
|
*
|
2019-02-04 01:08:18 +00:00
|
|
|
* Unify _Max_ with the largest integer _I_ such that `$VAR(I)` is a
|
2019-02-02 22:33:18 +00:00
|
|
|
* sub-term of _Term_.
|
|
|
|
*
|
|
|
|
* This built-in predicate is useful if part of a term has been grounded, and
|
|
|
|
* now you want to ground the full term.
|
|
|
|
*/
|
2019-02-04 01:08:18 +00:00
|
|
|
static Int largest_numbervar(USES_REGS1) {
|
2019-02-02 22:33:18 +00:00
|
|
|
return Yap_unify(MaxNumberedVar(Deref(ARG1), 2 PASS_REGS), ARG2);
|
|
|
|
}
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
static Term UNFOLD_LOOP(Term t, Term * b) {
|
2019-02-04 15:10:06 +00:00
|
|
|
Term os[2], o;
|
|
|
|
os[0] = o = MkVarTerm();
|
|
|
|
os[1] = t;
|
2019-02-13 09:44:24 +00:00
|
|
|
Term ti = Yap_MkApplTerm(FunctorEq, 2, os), t0 = *b;
|
|
|
|
*b = MkPairTerm(ti, t0);
|
2019-02-02 22:33:18 +00:00
|
|
|
return o;
|
|
|
|
}
|
2019-01-31 16:40:41 +00:00
|
|
|
|
2019-02-08 09:33:07 +00:00
|
|
|
typedef struct block_connector {
|
2019-02-13 09:44:24 +00:00
|
|
|
CELL * parent; //> index in the array;
|
2019-02-11 09:28:46 +00:00
|
|
|
Term source; //> source;
|
|
|
|
CELL *copy; //> copy;
|
|
|
|
CELL header; //> backup of first word of the source data;
|
|
|
|
CELL reference; //> term used to refer the copy.
|
2019-02-08 09:33:07 +00:00
|
|
|
} cl_connector;
|
2019-02-02 22:33:18 +00:00
|
|
|
|
2019-02-16 13:09:30 +00:00
|
|
|
static Int t_ref(cl_connector *d, cl_connector * q, Int *mep, Int max) {
|
|
|
|
if ( d >= q && d < q+max) {
|
|
|
|
*mep = d-q;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false; //&& d->source == (void *;
|
2019-02-09 09:43:26 +00:00
|
|
|
}
|
2019-02-08 15:01:12 +00:00
|
|
|
|
2019-02-13 09:44:24 +00:00
|
|
|
static Int create_entry(Term t, Int i, Int j, cl_connector * q, Int max) {
|
2019-02-08 09:33:07 +00:00
|
|
|
Term ref, h, *s, *ostart;
|
|
|
|
ssize_t n;
|
2019-02-15 21:09:58 +00:00
|
|
|
// fprintf(stderr,"[%ld,%ld]/%ld, %lx\n",i,j,max,t);
|
2019-02-16 13:09:30 +00:00
|
|
|
restart:
|
2019-02-08 15:01:12 +00:00
|
|
|
// first time, create a new term
|
2019-02-13 09:44:24 +00:00
|
|
|
if (IsVarTerm(t)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2019-02-08 09:33:07 +00:00
|
|
|
if (IsPairTerm(t)) {
|
2019-02-14 00:38:14 +00:00
|
|
|
Int me;
|
2019-02-08 15:01:12 +00:00
|
|
|
s = RepPair(t);
|
2019-02-13 09:44:24 +00:00
|
|
|
h = s[0];
|
2019-02-16 13:09:30 +00:00
|
|
|
if (IsAtomTerm(h) && t_ref((cl_connector *)AtomOfTerm(h), q, &me, max)) {
|
|
|
|
return me;
|
2019-02-13 09:44:24 +00:00
|
|
|
}
|
2019-02-11 09:28:46 +00:00
|
|
|
n = 2;
|
2019-02-13 09:44:24 +00:00
|
|
|
ostart = HR;
|
2019-02-08 15:01:12 +00:00
|
|
|
ref = AbsPair(ostart);
|
2019-02-13 09:44:24 +00:00
|
|
|
HR += 2;
|
2019-02-11 18:10:31 +00:00
|
|
|
} else if (IsApplTerm(t)) {
|
2019-02-14 00:38:14 +00:00
|
|
|
Int me;
|
2019-02-08 09:33:07 +00:00
|
|
|
h = (CELL)FunctorOfTerm(t);
|
2019-02-13 09:44:24 +00:00
|
|
|
if (IsExtensionFunctor((Functor)h)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
if (IsAtomTerm(h) &&
|
2019-02-16 13:09:30 +00:00
|
|
|
t_ref((cl_connector*)AtomOfTerm(h),q,&me,max)) {
|
2019-02-14 00:38:14 +00:00
|
|
|
return me;
|
2019-02-13 09:44:24 +00:00
|
|
|
}
|
2019-02-16 13:09:30 +00:00
|
|
|
n = ArityOfFunctor((Functor)h);
|
|
|
|
s = RepAppl(t);
|
2019-02-13 09:44:24 +00:00
|
|
|
ostart = HR;
|
2019-02-08 09:33:07 +00:00
|
|
|
ref = AbsAppl(ostart);
|
|
|
|
*ostart++ = s[0];
|
2019-02-13 09:44:24 +00:00
|
|
|
HR=ostart+n;
|
2019-02-14 00:38:14 +00:00
|
|
|
} else {
|
|
|
|
Int me;
|
2019-02-16 13:09:30 +00:00
|
|
|
if (IsAtomTerm(t) && t_ref((cl_connector*)AtomOfTerm(t),q,&me,max)) {
|
|
|
|
t = q[me].source;
|
|
|
|
goto restart;
|
|
|
|
} else {
|
2019-02-13 09:44:24 +00:00
|
|
|
return -1;
|
2019-02-16 13:09:30 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
q[max].header = h;
|
2019-02-13 09:44:24 +00:00
|
|
|
q[max].parent = q[i].copy+j;
|
2019-02-16 13:09:30 +00:00
|
|
|
q[i].copy[j] = ref;
|
2019-02-08 15:01:12 +00:00
|
|
|
q[max].source = t;
|
|
|
|
q[max].copy = ostart;
|
|
|
|
q[max].reference = ref;
|
2019-02-13 09:44:24 +00:00
|
|
|
s[0] = MkAtomTerm((void*)(q+max));
|
|
|
|
return max+1;
|
2019-02-01 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Int cp_link(Term t, Int i, Int j, cl_connector * q, Int max, CELL * tailp) {
|
2019-02-08 15:01:12 +00:00
|
|
|
Int me;
|
2019-02-13 09:44:24 +00:00
|
|
|
t = Deref(t);
|
2019-02-08 15:01:12 +00:00
|
|
|
if ((me = create_entry(t, i, j, q, max)) < max) {
|
2019-02-13 09:44:24 +00:00
|
|
|
if (me < 0) {
|
|
|
|
q[i].copy[j] = t;
|
|
|
|
return max;
|
|
|
|
}
|
2019-02-16 13:09:30 +00:00
|
|
|
Term ref = q[me].reference;
|
2019-02-08 15:01:12 +00:00
|
|
|
if (IsVarTerm(ref)) {
|
|
|
|
q[i].copy[j] = ref;
|
2019-02-16 13:09:30 +00:00
|
|
|
// fprintf(stderr," - %p\n", ref);
|
2019-05-04 10:45:42 +01:00
|
|
|
}
|
2019-02-15 23:04:35 +00:00
|
|
|
else {
|
2019-02-14 00:38:14 +00:00
|
|
|
Term v = UNFOLD_LOOP(ref, tailp);
|
|
|
|
q[i].copy[j] = v;
|
2019-02-15 23:04:35 +00:00
|
|
|
if (me)
|
2019-02-14 00:38:14 +00:00
|
|
|
q[me].parent[0] = v;
|
2019-02-15 23:04:35 +00:00
|
|
|
q[me].reference = v;
|
2019-02-08 15:01:12 +00:00
|
|
|
}
|
2019-02-14 00:38:14 +00:00
|
|
|
return max;
|
|
|
|
}
|
|
|
|
return me;
|
2019-02-08 15:01:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Term Yap_BreakCycles(Term inp, UInt arity, Term * listp USES_REGS) {
|
2019-02-02 22:33:18 +00:00
|
|
|
|
2019-02-08 09:33:07 +00:00
|
|
|
int lvl = push_text_stack();
|
|
|
|
|
|
|
|
Term t = Deref(inp);
|
2019-02-11 09:28:46 +00:00
|
|
|
ssize_t qsize = 2048, qlen = 0;
|
2019-02-08 15:01:12 +00:00
|
|
|
cl_connector *q = Malloc(qsize * sizeof(cl_connector));
|
2019-02-08 09:33:07 +00:00
|
|
|
Term *s;
|
2019-02-11 09:28:46 +00:00
|
|
|
Int i = 0;
|
|
|
|
|
|
|
|
HB = HR;
|
2019-02-13 09:44:24 +00:00
|
|
|
qlen = 0;
|
2019-02-16 13:09:30 +00:00
|
|
|
Term t0 = MkPairTerm(t, TermNil);
|
|
|
|
q[0].copy = HR;
|
|
|
|
HR+=2;
|
2019-02-13 09:44:24 +00:00
|
|
|
if (IsVarTerm(t) || IsPrimitiveTerm(t)) {
|
2019-02-01 13:14:33 +00:00
|
|
|
return t;
|
2019-02-08 09:33:07 +00:00
|
|
|
} else {
|
2019-02-08 15:01:12 +00:00
|
|
|
// initialization
|
2019-02-16 13:09:30 +00:00
|
|
|
qlen = create_entry(Deref(t0), i, 0, q, qlen);
|
2019-02-13 09:44:24 +00:00
|
|
|
while(i<qlen) {
|
2019-02-08 09:33:07 +00:00
|
|
|
arity_t n, j;
|
2019-02-11 09:28:46 +00:00
|
|
|
if (IsPairTerm(q[i].source)) {
|
2019-02-14 00:38:14 +00:00
|
|
|
s = RepPair(q[i].source);
|
|
|
|
n = 2;
|
2019-02-08 15:01:12 +00:00
|
|
|
// fetch using header field.
|
2019-02-14 00:38:14 +00:00
|
|
|
qlen = cp_link(q[i].header, i, 0, q, qlen, listp);
|
2019-02-08 15:01:12 +00:00
|
|
|
// fetch using standard access
|
2019-02-14 00:38:14 +00:00
|
|
|
qlen = cp_link(s[1], i, 1, q, qlen, listp);
|
|
|
|
} else {
|
|
|
|
s = RepAppl(q[i].source) + 1;
|
|
|
|
n = ArityOfFunctor((Functor)q[i].header);
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
qlen = cp_link(s[j], i, j, q, qlen, listp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i< qlen; i++) {
|
|
|
|
CELL *p = IsPairTerm(q[i].source) ? RepPair(q[i].source) : RepAppl(q[i].source);
|
2019-02-16 13:09:30 +00:00
|
|
|
p[0] = (q[i].header);
|
2019-02-14 00:38:14 +00:00
|
|
|
}
|
2019-02-14 07:44:31 +00:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
pop_text_stack(lvl);
|
2019-02-08 09:33:07 +00:00
|
|
|
|
2019-02-14 00:38:14 +00:00
|
|
|
HB = B->cp_h;
|
2019-02-16 13:09:30 +00:00
|
|
|
return HeadOfTerm( q[0].reference );
|
2019-02-01 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
/** @pred rational_term_to_tree(? _TI_,- _TF_, ?SubTerms, ?MoreSubterms)
|
2019-02-02 22:33:18 +00:00
|
|
|
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
The term _TF_ is a forest representation (without cycles) for
|
|
|
|
the Prolog term _TI_. The term _TF_ is the main term. The
|
|
|
|
difference list _SubTerms_-_MoreSubterms_ stores terms of the
|
|
|
|
form _V=T_, where _V_ is a new variable occuring in _TF_, and
|
|
|
|
_T_ is a copy of a sub-term from _TI_.
|
2019-02-02 22:33:18 +00:00
|
|
|
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
*/
|
2019-02-13 09:44:24 +00:00
|
|
|
static Int rational_term_to_tree(USES_REGS1) {
|
2019-02-08 09:33:07 +00:00
|
|
|
Term t = Deref(ARG1);
|
2019-02-06 00:08:15 +00:00
|
|
|
Term l = Deref(ARG4);
|
2019-02-04 01:08:18 +00:00
|
|
|
if (IsVarTerm(l))
|
|
|
|
Yap_unify(l, MkVarTerm());
|
2019-02-06 00:08:15 +00:00
|
|
|
return Yap_unify(Yap_BreakCycles(t, 4, &l PASS_REGS), ARG2) &&
|
2019-02-14 00:38:14 +00:00
|
|
|
Yap_unify(l, ARG3);
|
2019-02-02 22:33:18 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 13:14:33 +00:00
|
|
|
void Yap_InitTermCPreds(void) {
|
2019-02-13 09:44:24 +00:00
|
|
|
Yap_InitCPred("cycles_in_term", 2, cycles_in_term, 0);
|
2019-02-17 23:19:26 +00:00
|
|
|
Yap_InitCPred("term_variables", 2, term_variables, 0);
|
|
|
|
Yap_InitCPred("term_variables", 3, term_variables3, 0);
|
2019-02-04 01:08:18 +00:00
|
|
|
Yap_InitCPred("$variables_in_term", 3, variables_in_term, 0);
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-21 20:19:31 +00:00
|
|
|
Yap_InitCPred("$free_variables_in_term", 3, free_variables_in_term, 0);
|
|
|
|
Yap_InitCPred("free_variables_in_term", 3, free_variables_in_term, 0);
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-21 21:03:44 +00:00
|
|
|
Yap_InitCPred("term_attvars", 2, term_attvars, 0);
|
2019-01-30 15:24:06 +00:00
|
|
|
|
|
|
|
CurrentModule = TERMS_MODULE;
|
2019-02-01 13:14:33 +00:00
|
|
|
Yap_InitCPred("variable_in_term", 2, variable_in_term, 0);
|
2019-01-30 15:24:06 +00:00
|
|
|
Yap_InitCPred("variables_within_term", 3, p_variables_within_term, 0);
|
|
|
|
Yap_InitCPred("new_variables_in_term", 3, p_new_variables_in_term, 0);
|
|
|
|
CurrentModule = PROLOG_MODULE;
|
2019-02-13 09:44:24 +00:00
|
|
|
Yap_InitCPred("rational_term_to_tree", 4, rational_term_to_tree, 0);
|
2019-02-01 13:14:33 +00:00
|
|
|
|
2019-01-30 15:24:06 +00:00
|
|
|
Yap_InitCPred("$non_singletons_in_term", 3, p_non_singletons_in_term, 0);
|
|
|
|
|
2019-02-04 01:08:18 +00:00
|
|
|
Yap_InitCPred("ground", 1, ground, SafePredFlag);
|
|
|
|
Yap_InitCPred("cyclic_term", 1, cyclic_term, SafePredFlag);
|
2019-01-30 15:24:06 +00:00
|
|
|
|
2019-02-11 09:28:46 +00:00
|
|
|
Yap_InitCPred("numbervars", 3, p_numbervars, 0);
|
2019-02-02 22:33:18 +00:00
|
|
|
Yap_InitCPred("largest_numbervar", 2, largest_numbervar, 0);
|
2019-01-30 15:24:06 +00:00
|
|
|
}
|
2019-02-13 09:44:24 +00:00
|
|
|
//@}
|