RosettaCodeData/Task/Balanced-ternary/Prolog/balanced-ternary-2.pro

124 lines
2.8 KiB
Prolog

:- module('bt_add.pl', [bt_add/3,
bt_add1/3,
op(900, xfx, btplus),
op(900, xfx, btmoins),
btplus/2,
btmoins/2,
strip_nombre/3
]).
:- op(900, xfx, btplus).
:- op(900, xfx, btmoins).
% define operator btplus
A is X btplus Y :-
bt_add(X, Y, A).
% define operator btmoins
% no need to define a predicate for the substraction
A is X btmoins Y :-
X is Y btplus A.
% bt_add(?X, ?Y, ?R)
% R is X + Y
% X, Y, R are strings
% At least 2 args must be instantiated
bt_add(X, Y, R) :-
( nonvar(X) -> string_to_list(X, X1); true),
( nonvar(Y) -> string_to_list(Y, Y1); true),
( nonvar(R) -> string_to_list(R, R1); true),
bt_add1(X1, Y1, R1),
( var(X) -> string_to_list(X, X1); true),
( var(Y) -> string_to_list(Y, Y1); true),
( var(R) -> string_to_list(R, R1); true).
% bt_add1(?X, ?Y, ?R)
% R is X + Y
% X, Y, R are lists
bt_add1(X, Y, R) :-
% initialisation : X and Y must have the same length
% we add zeros at the beginning of the shortest list
( nonvar(X) -> length(X, LX); length(R, LR)),
( nonvar(Y) -> length(Y, LY); length(R, LR)),
( var(X) -> LX is max(LY, LR) , length(X1, LX), Y1 = Y ; X1 = X),
( var(Y) -> LY is max(LX, LR) , length(Y1, LY), X1 = X ; Y1 = Y),
Delta is abs(LX - LY),
( LX < LY -> normalise(Delta, X1, X2), Y1 = Y2
; LY < LX -> normalise(Delta, Y1, Y2), X1 = X2
; X1 = X2, Y1 = Y2),
% if R is instancied, it must have, at least, the same length than X or Y
Max is max(LX, LY),
( (nonvar(R), length(R, LR), LR < Max) -> Delta1 is Max - LR, normalise(Delta1, R, R2)
; nonvar(R) -> R = R2
; true),
bt_add(X2, Y2, C, R2),
( C = 48 -> strip_nombre(R2, R, []),
( var(X) -> strip_nombre(X2, X, []) ; true),
( var(Y) -> strip_nombre(Y2, Y, []) ; true)
; var(R) -> strip_nombre([C|R2], R, [])
; ( select(C, [45,43], [Ca]),
( var(X) -> strip_nombre([Ca | X2], X, [])
; strip_nombre([Ca | Y2], Y, [])))).
% here we actually compute the sum
bt_add([], [], 48, []).
bt_add([H1|T1], [H2|T2], C3, [R2 | L]) :-
bt_add(T1, T2, C, L),
% add HH1 and H2
ternary_sum(H1, H2, R1, C1),
% add first carry,
ternary_sum(R1, C, R2, C2),
% add second carry
ternary_sum(C1, C2, C3, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ternary_sum
% @arg1 : V1
% @arg2 : V2
% @arg3 : R is V1 + V2
% @arg4 : Carry
ternary_sum(43, 43, 45, 43).
ternary_sum(43, 45, 48, 48).
ternary_sum(45, 43, 48, 48).
ternary_sum(45, 45, 43, 45).
ternary_sum(X, 48, X, 48).
ternary_sum(48, X, X, 48).
% if L has a length smaller than N, complete L with 0 (code 48)
normalise(0, L, L) :- !.
normalise(N, L1, L) :-
N1 is N - 1,
normalise(N1, [48 | L1], L).
% contrary of normalise
% remove leading zeros.
% special case of number 0 !
strip_nombre([48]) --> {!}, "0".
% enlève les zéros inutiles
strip_nombre([48 | L]) -->
strip_nombre(L).
strip_nombre(L) -->
L.