166 lines
3.6 KiB
Prolog
166 lines
3.6 KiB
Prolog
/** Predicates that generate integers
|
|
|
|
These predicates can be used to reason about integers in a reduced domain that
|
|
follow some property. `library(clpz)` provides another way of reasoning about
|
|
integers that may also be interesting.
|
|
*/
|
|
|
|
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
|
|
|
|
%% TODO: numlist/5.
|
|
|
|
:- use_module(library(lists), [length/2]).
|
|
:- use_module(library(error)).
|
|
|
|
%% between(+Lower, +Upper, ?X).
|
|
%
|
|
% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_.
|
|
% Can be used both to check if X is between Lower and Upper or to generate an integer between
|
|
% Lower and Upper.
|
|
%
|
|
% Examples:
|
|
%
|
|
% ```
|
|
% ?- between(10, 20, 15).
|
|
% true.
|
|
% ?- between(10, 20, 25).
|
|
% false.
|
|
% ?- between(3, 5, X).
|
|
% X = 3
|
|
% ; X = 4
|
|
% ; X = 5.
|
|
% ```
|
|
between(Lower, Upper, X) :-
|
|
must_be(integer, Lower),
|
|
must_be(integer, Upper),
|
|
can_be(integer, X),
|
|
( nonvar(X) ->
|
|
Lower =< X,
|
|
X =< Upper
|
|
; Lower =< Upper,
|
|
between_(Lower, Upper, X)
|
|
).
|
|
|
|
between_(Lower, Upper, Lower1) :-
|
|
Lower < Upper,
|
|
!,
|
|
( Lower1 = Lower
|
|
; Lower0 is Lower + 1,
|
|
between_(Lower0, Upper, Lower1)
|
|
).
|
|
between_(Lower, Lower, Lower).
|
|
|
|
enumerate_nats(I, I).
|
|
enumerate_nats(I0, N) :-
|
|
I1 is I0 + 1,
|
|
enumerate_nats(I1, N).
|
|
|
|
%% gen_nat(?N)
|
|
%
|
|
% True iff N is a natural number.
|
|
gen_nat(N) :-
|
|
can_be(integer, N),
|
|
( var(N) -> enumerate_nats(0, N)
|
|
; true
|
|
).
|
|
|
|
enumerate_ints(I, I).
|
|
enumerate_ints(I0, N) :-
|
|
I0 > 0,
|
|
N is -I0.
|
|
enumerate_ints(I0, N) :-
|
|
I1 is I0 + 1,
|
|
enumerate_ints(I1, N).
|
|
|
|
%% gen_int(?N)
|
|
%
|
|
% True iff N is an integer.
|
|
gen_int(N) :-
|
|
can_be(integer, N),
|
|
( var(N) -> enumerate_ints(0, N)
|
|
; true
|
|
).
|
|
|
|
repeat_integer(N) :-
|
|
N > 0.
|
|
repeat_integer(N0) :-
|
|
N0 > 0, N1 is N0 - 1, repeat_integer(N1).
|
|
|
|
%% repeat(+N)
|
|
%
|
|
% Succeeds N times. This predicate is only included for compatibility and *should not be used*
|
|
% because it lacks a declarative interpretation.
|
|
repeat(N) :-
|
|
must_be(integer, N), repeat_integer(N).
|
|
|
|
%% numlist(?Upper, ?List)
|
|
%
|
|
% True iff List is the list of integers _[1, ..., Upper]_. Example:
|
|
%
|
|
% ```
|
|
% ?- numlist(X, Y).
|
|
% X = 1, Y = [1],
|
|
% ; X = 2, Y = [1,2]
|
|
% ; X = 3, Y = [1,2,3]
|
|
% ; ... .
|
|
% ```
|
|
numlist(Upper, List) :-
|
|
( integer(Upper) -> findall(X, between(1, Upper, X), List)
|
|
; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List)
|
|
).
|
|
|
|
diag_nats(M, N, M, N).
|
|
diag_nats(M, 0, M1, N1) :-
|
|
!,
|
|
M0 is M+1,
|
|
diag_nats(0, M0, M1, N1).
|
|
diag_nats(M, N, M1, N1) :-
|
|
M0 is M+1,
|
|
N0 is N-1,
|
|
diag_nats(M0, N0, M1, N1).
|
|
|
|
diag_nats(0, 0).
|
|
diag_nats(M, N) :-
|
|
diag_nats(0, 1, M, N).
|
|
|
|
diag_nats_signs(0, 0, 0, 0) :- !.
|
|
diag_nats_signs(0, M, 0, M0) :- !,
|
|
( M0 = M ; M0 is -M ).
|
|
diag_nats_signs(M, 0, M0, 0) :- !,
|
|
( M0 = M ; M0 is -M ).
|
|
diag_nats_signs(M, N, M, N).
|
|
diag_nats_signs(M, N, M, N0) :-
|
|
N0 is -N.
|
|
diag_nats_signs(M, N, M0, N) :-
|
|
M0 is -M.
|
|
diag_nats_signs(M, N, M0, N0) :-
|
|
M0 is -M, N0 is -N.
|
|
|
|
diag_ints(M, N, M0, N0) :-
|
|
diag_nats(M, N),
|
|
diag_nats_signs(M, N, M0, N0).
|
|
|
|
diag_ints(M, N) :-
|
|
diag_ints(_, _, M, N).
|
|
|
|
gen_ints(L, U) :-
|
|
can_be(integer, L), can_be(integer, U),
|
|
( integer(L), integer(U), !
|
|
; integer(L) -> gen_int(U)
|
|
; integer(U) -> gen_int(L)
|
|
; diag_ints(L, U)
|
|
),
|
|
L =< U.
|
|
|
|
%% numlist(?Lower, ?Upper, ?List).
|
|
%
|
|
% True iff List is a list of the form _[Lower, ..., Upper]_.
|
|
% Example:
|
|
%
|
|
% ```
|
|
% ?- numlist(5, 10, X).
|
|
% X = [5,6,7,8,9,10].
|
|
% ```
|
|
numlist(Lower, Upper, List) :-
|
|
gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List).
|