34
   35:- module(near_utils,
   36          [fact_near/1,
   37           fact_near/2,
   38           retract_near/1,
   39           retractall_near/1,
   40           real_near/2,
   41           real_compare/3,
   42           near_compare/3,
   43           unify_near/2,
   44           equiv_near/2,
   45           repsilon/1,
   46           repsilon/2]).   47
   48:- use_module(library(apply)).   49:- use_module(library(call_ref)).   50:- use_module(library(mapargs)).   51:- use_module(library(compare_eq)).   52
   53:- meta_predicate
   54        fact_near(0 ),
   55        fact_near(0, -),
   56        retract_near(0 ),
   57        retractall_near(0 ).   58
   60
   61fact_near(M:Call) :-
   62    freeze_near(Call, Mask),
   63    M:Mask,
   64    frozen_near(Mask).
   65
   66fact_near(Call, Ref) :-
   67    freeze_near(Call, Mask),
   68    call_ref(Mask, Ref),
   69    frozen_near(Mask).
   70
   71retract_near(Call) :-
   72    fact_near(Call, Ref),
   73    erase(Ref).
   74
   75retractall_near(Call) :-
   76    forall(( freeze_near(Call, Mask),
   77             call_ref(Mask, Ref)
   78           ),
   79           erase(Ref)).
   80
   81real(R) :-
   82    ( R == 1.5NaN
   83    ->fail
   84    ; float(R)
   85    ->true
   86    ; rational(R),
   87      \+ integer(R)
   88    ).
   89
   90rnum(R) :-
   91    ( R == 1.5NaN
   92    ->fail
   93    ; float(R)
   94    ->true
   95    ; rational(R)
   96    ).
   97
   98attr_unify_hook(near(Arg1), Arg) :-
   99    rnum(Arg),
  100    real_near(Arg1, Arg).
  101
  102put_near(Arg1, Arg) :-
  103    ( nonvar(Arg1)
  104    ->put_attr(Arg, near_utils, near(Arg1))
  105    ; Arg = Arg1
  106    ).
  107
  108freeze_near(Arg1, Arg) :-
  109    ( real(Arg1)
  110    ->put_near(Arg1, Arg)
  111      112      113      114      115      116      117      118      119      120      121    ; var(Arg1)
  122    ->Arg = Arg1
  123    ; mapargs(freeze_near, Arg1, Arg)
  124    ).
  125
  126frozen_near(Mask) :-
  127    term_attvars(Mask, Vars),
  128    maplist(frozen_near_1, Vars).
  129
  130frozen_near_1(Var) :-
  131    ( get_attr(Var, near_utils, near(Val))
  132    ->del_attr(Var, near_utils),
  133      Var = Val
  134    ; true
  135    ).
  136
  137real_near(A, B) :- near_compare(=, A, B).
  138
  139real_compare(A, C, B) :- near_compare(C, A, B).
  140
  141repsilon(E) :- E is 1024*epsilon.
  142
  143repsilon(N, E) :-
  144    repsilon(R),
  145    E is R*N.
  146
  147near_compare(Comparator, A, B) :-
  148    ( A =:= B
  149    ->compare_eq(Comparator)
  150    ; repsilon(max(abs(A), abs(B)), E),
  151      compare(Comparator, A, B, E)
  152    ).
  153
  154compare(=,  A, B, E) :- abs(A - B) =< E.
  155compare(=<, A, B, E) :- A - B =< E.
  156compare(>=, A, B, E) :- B - A =< E.
  157compare(<,  A, B, E) :- B - A >  E.
  158compare(>,  A, B, E) :- A - B >  E.
  159compare(\=, A, B, E) :- abs(A - B) > E.
  160
  162unify_near(Arg1, Arg2) :-
  163    ( ( var(Arg1)
  164      ; var(Arg2)
  165      ; integer(Arg1),
  166        integer(Arg2)
  167      )
  168    ->Arg1 = Arg2
  169    ; rnum(Arg1),
  170      rnum(Arg2)
  171    ->real_near(Arg1, Arg2)
  172    ; mapargs(unify_near, Arg1, Arg2)
  173    ).
  174
  176equiv_near(Arg1, Arg2) :-
  177    ( ( var(Arg1)
  178      ; var(Arg2)
  179      ; integer(Arg1),
  180        integer(Arg2)
  181      )
  182    ->Arg1 == Arg2
  183    ; rnum(Arg1),
  184      rnum(Arg2)
  185    ->real_near(Arg1, Arg2)
  186    ; mapargs(equiv_near, Arg1, Arg2)
  187    )