.decl infs(x: float)
.output infs
infs(100000.0 ^ 100000.0).
infs(-x) :- infs(x).

.decl nans(x: float)
.output nans
nans(0.0 / 0.0).
nans(x + y) :- infs(x), infs(y), x + y != x + y.

.decl floats_finite(x: float)
floats_finite(-20.0).
floats_finite(-1.5).
floats_finite(-1.0).
floats_finite(-0.0).
floats_finite(0.0).
floats_finite(1.0).
floats_finite(1.5).
floats_finite(20.0).

.decl signed(x: number)
signed(to_number(x)) :- floats_finite(x).

.decl unsigned(x: unsigned)
unsigned(to_unsigned(x)) :- floats_finite(x), 0.0 <= x.

.decl floats(x: float)
floats(x) :- floats_finite(x); nans(x); infs(x).

.decl letters(x: symbol)
letters("a").
letters("`").

.decl symbols(x: symbol)
symbols(x) :- letters(x).
symbols(cat(x, y)) :- letters(x), letters(y).
// FIXME: need `to_string` overloads for other numeric types
symbols(to_string(x)) :- signed(x). //floats(x).


#define eval_cmp(ty, in)              \
  .decl in##_pairs(x: ty, y: ty)      \
  in##_pairs(x, y) :- in(x), in(y).   \
  eval_cmp_(cmp_##ty, ty, in##_pairs)

#define eval_cmp_(out, ty, in)         \
  .decl out(op: symbol, x: ty, y: ty)  \
  .output out                          \
  out("< ", x, y) :- in(x, y), x <  y. \
  out("<=", x, y) :- in(x, y), x <= y. \
  out("> ", x, y) :- in(x, y), x >  y. \
  out(">=", x, y) :- in(x, y), x >= y. \
  out(" =", x, y) :- in(x, y), x  = y. \
  out("!=", x, y) :- in(x, y), x != y.

eval_cmp(number   , signed)
eval_cmp(unsigned , unsigned)
eval_cmp(float    , floats)
eval_cmp(symbol   , symbols)
