1:- module(math, [factorial/2, base_digits/2, base_conv/4, to_base_10/3,
    2                 to_digits/2, to_digits/3, to_digits/4,
    3                 from_digits/2, from_digits/4, from_digits/5,
    4                 lcm/3, gcd/3, first_numbers/2, first_numbers/3, sumf/2, averagef/2,
    5                 seq/2, arith_seq/2, geom_seq/2, increasing/1]).    6
    7:- use_module(library(clpfd)).    8
    9:- use_module(utility).   10
   11factorial(0, 1).
   12factorial(N, F) :-
   13    N1 #= N - 1,
   14    factorial(N1, F1),
   15    F #= N * F1.
   16
   17base_digits(Base, BaseDigits) :-
   18    numbers(Numbers),
   19    letters_lower(LowerLetters),
   20    letters_upper(UpperLetters),
   21    flatten([Numbers, LowerLetters, UpperLetters], DigitList),
   22    take(Base, DigitList, BaseDigits).
   23
   24from_digits(NumDigits, Out) :-
   25    base_digits(10, FromDigits),
   26    from_digits(10, FromDigits, NumDigits, Out).
   27from_digits(FromBase, FromDigits, NumDigits, Out) :-
   28    foldl(from_digits(FromBase, FromDigits), NumDigits, 0, Out).
   29from_digits(Base, BaseDigits, Digit, Cur, Out) :-
   30    nth0(DigitVal, BaseDigits, Digit),
   31    Out #= Base * Cur + DigitVal.
   32
   33to_digits(N, Digits) :- base_digits(10, Ds), to_digits(10, Ds, N, Digits).
   34to_digits(Base, N, Digits) :-
   35    base_digits(Base, BaseDigits),
   36    to_digits(Base, BaseDigits, N, Digits).
   37to_digits(Base, BaseDigits, N, Digits) :-
   38    N #< Base,
   39    nth0(N, BaseDigits, NChar),
   40    Digits = [NChar];
   41
   42    N #>= Base,
   43    DVal #= N mod Base,
   44    NewN #= N div Base,
   45
   46    to_digits(Base, BaseDigits, NewN, Rest),
   47    nth0(DVal, BaseDigits, D),
   48    append(Rest, [D], Digits).
   49
   50to_base_10(FromBase, N, M) :-
   51    base_conv(FromBase, 10, N, TempM),
   52    term_to_atom(M, TempM).
   53
   54base_conv(FromBase, ToBase, N, M) :-
   55    base_digits(FromBase, FromDigits),
   56
   57    (
   58        atom(N),
   59        atom_chars(N, NAtom);
   60
   61        not(atom(N)),
   62        term_to_atom(N, TempAtom),
   63        atom_chars(TempAtom, NAtom)
   64    ),
   65
   66    from_digits(FromBase, FromDigits, NAtom, Base10),
   67
   68    to_digits(ToBase, Base10, MAtom),
   69    atom_chars(M, MAtom).
   70
   71seq(_, []).
   72seq(_, [_]).
   73seq(Pred, [Start, Next|Rest]) :-
   74    call(Pred, Next, Start),
   75    seq(Pred, [Next|Rest]).
   76
   77increasing([]).
   78increasing([_]).
   79increasing([A,B|Tail]) :-
   80    A #=< B,
   81    increasing([B|Tail]).
   82
   83arith_seq_f(Inc, Next, Start) :- Next #= Start + Inc.
   84arith_seq(Inc, Seq) :- seq(arith_seq_f(Inc), Seq).
   85
   86geom_seq_f(M, Next, Start) :- Next #= M * Start.
   87geom_seq(M, Seq) :- seq(geom_seq_f(M), Seq).
   88
   89first_numbers(_, _, []).
   90first_numbers(Property, X, [X|T]) :-
   91    call(Property, X),
   92    Next #= X + 1,
   93    first_numbers(Property, Next, T).
   94first_numbers(Property, X, T) :-
   95    not(call(Property, X)),
   96    Next #= X + 1,
   97    first_numbers(Property, Next, T).
   98
   99first_numbers(Property, Numbers) :- first_numbers(Property, 0, Numbers).
  100
  101lcm(A, B, L) :-
  102    gcd(A, B, G),
  103    L * G #= A * B.
  104
  105gcd(A, 0, A).
  106gcd(A, B, G) :-
  107    NewB #= A mod B,
  108    gcd(B, NewB, G).
  109
  110sumf([], 0.0).
  111sumf([H|T], S) :-
  112    sumf(T, SumT),
  113    S is SumT + H.
  114
  115averagef(Values, Average) :-
  116    sumf(Values, Sum),
  117    length(Values, L),
  118    Average is Sum / float(L)