```    1:- encoding(utf8).
2:- module(
3  math_ext,
4  [
5    avg_list/2,           % +Numbers, -Avg
6    between/4,            % +Low, +High, +Interval, ?Value
7    decimal_parts/3,      % ?Decimal, ?Integer, ?Fractorial
8    fractional_integer/2, % +Number, -Fractorial
9    fractional_weights/2, % ?Fractional, ?Weights
10    inf_max/3,            % +X, +Y, -Z
11    inf_min/3,            % +X, +Y, -Z
12    integer_weights/2,    % ?Integer, ?Weights
13    integer_weights/3     % ?Integer, +Base, ?Weights
14  ]
15).```

# Mathematics extensions

*/

`   21:- use_module(library(aggregate)).   22:- use_module(library(clpfd)).   23:- use_module(library(error)).   24:- use_module(library(lists)).   25:- use_module(library(plunit)).`
avg_list(+Numbers:list(number), +Avg:number) is semidet
avg_list(+Numbers:list(number), -Avg:number) is det

# Examples

```?- avg_list([1 rdiv 3, 1 rdiv 6], X).
X = 1 rdiv 4.```

# Special cases

Avg is the integer 0 in case Numbers is the empty list. This is in line with how sum_list/2 works.

throws
- instantiation_error if Numbers is non-ground.
```   48avg_list([], 0):- !.
49avg_list(L, Avg):-
50  aggregate(count+sum(X), member(X, L), Len+Sum),
51  Avg is Sum / Len.```
between(+Low:integer, +High:integer, +Interval:integer, +Value:integer) is semidet
between(+Low:integer, +High:integer, +Interval:integer, -Value:integer) is nondet
```   60between(Low, _, _, Low).
61between(Low1, High, Interval, Value):-
62  Low2 is Low1 + Interval,
63  (High == â -> true ; Low2 =< High),  between(Low2, High, Interval, Value).```
decimal_parts(+Decimal:number, -Integer:integer, -Frac:nonneg) is det
decimal_parts(-Decimal:number, +Integer:integer, +Frac:nonneg) is det
throws
- instantation_error
- type_error
```   74decimal_parts(N, Int, Frac):-
75  ground(N),
76  must_be(number, N), !,
77  Int is floor(float_integer_part(N)),
78  fractional_integer(N, Frac).
79decimal_parts(N, Int, Frac):-
80  must_be(integer, Int),
81  must_be(nonneg, Frac),
82  number_length(Frac, Length),
83  N is copysign(abs(Int) + (Frac rdiv (10 ^ Length)), Int).```
fractional_integer(+Frac:number, -Int:integer) is det
Variant of float_fractional_part/2 where the integer value instead of the fractional part is returned.
```   92fractional_integer(Frac, _) :-
93  \+ ground(Frac), !,
94  instantiation_error(Frac).
95fractional_integer(Frac, Int) :-
96  Frac = A rdiv B, !,
97  FloatFrac is A / B,
98  fractional_integer(FloatFrac, Int).
99fractional_integer(Frac, Int) :-
100  atom_number(FracAtom, Frac),
101  % We assume that there is at most one occurrence of `.'.
102  sub_atom(FracAtom, IndexOfDot, 1, _, .), !,
103  Skip is IndexOfDot + 1,
104  sub_atom(FracAtom, Skip, _, 0, IntAtom),
105  atom_number(IntAtom, Int).
106fractional_integer(_, 0).```
fractional_weights(-Frac:number, +Weights:list(between(0,9))) is det
fractional_weights(+Frac:number, -Weights:list(between(0,9))) is det
```  113fractional_weights(Frac, Weights):-
114  ground(Weights), !,
115  aggregate_all(
116    sum(N),
117    (
118      nth1(Position, Weights, Weight),
119      N is Weight rdiv (10 ^ Position)
120    ),
121    Frac
122  ).
123fractional_weights(Frac, Weights):-
124  ground(Frac), !,
125  fractional_integer(Frac, Int),
126  integer_weights(Int, Weights).
127fractional_weights(_, _):-
128  instantiation_error(_).
129
130
131
132%! inf_max(+X, +Y, -Z) is det.
133
134inf_max(â, _, â) :- !.
135inf_max(_, â, â) :- !.
136inf_max(-â, Y, Y) :- !.
137inf_max(X, -â, X) :- !.
138inf_max(X, Y, Z) :-
139  Z is max(X, Y).
140
141
142
143%! inf_min(+X, +Y, -Z) is det.
144
145inf_min(-â, _, -â) :- !.
146inf_min(_, -â, -â) :- !.
147inf_min(â, Y, Y) :- !.
148inf_min(X, â, X) :- !.
149inf_min(X, Y, Z) :-
150  Z is min(X, Y).```
integer_weights(+Int:nonneg, +Weights:list(between(0,9))) is semidet
integer_weights(-Int:nonneg, +Weights:list(between(0,9))) is det
integer_weights(+Int:nonneg, -Weights:list(between(0,9))) is det
```  158integer_weights(Int, Weights):-
159  integer_weights(Int, 10, Weights).```
integer_weights(+Int:nonneg, +Base:nonneg, +Weights:list(between(0,9))) is semidet
integer_weights(-Int:nonneg, +Base:nonneg, +Weights:list(between(0,9))) is det
integer_weights(+Int:nonneg, +Base:nonneg, -Weights:list(between(0,9))) is det
- http://stackoverflow.com/questions/4192063/reversible-binary-to-number-predicate/28442760#28442760
```  168integer_weights(Int, Base, Weights):-
169  (nonvar(Int), nonvar(Base) ; nonvar(Weights)), !,
170  integer_weights0(Int, Base, Weights, 0, Int).
171
172integer_weights0(Int, _, [], Int, _) :- !.
173integer_weights0(Int, Base, [Weight|Weights], Int0, M):-
174  in_base(Weight, Base),
175  Int1 #= Weight + Base * Int0,
176  M #>= Int1,
177  integer_weights0(Int, Base, Weights, Int1, M).
178
179in_base(Weight, Base):-
180  Base #= Max + 1,
181  Weight in 0..Max.
182
183:- begin_tests('integer_weights/3').  184
185test(
186  'integer_weights(+,+,+) is semidet. TRUE',
187  [forall(integer_weights_test(Int,Base,Weights))]
188):-
189  integer_weights(Int, Base, Weights).
190test(
191  'integer_weights(+,+,-) is multi. TRUE',
192  [forall(integer_weights_test(Int,Base,Weights)),nondet]
193):-
194  integer_weights(Int, Base, Weights0),
195  Weights0 = Weights.
196test(
197  'integer_weights(-,+,+) is multi. TRUE',
198  [forall(integer_weights_test(Int, Base, Weights)),nondet]
199):-
200  integer_weights(Int, Base, Weights0),
201  Weights0 = Weights.
202
203integer_weights_test(1226, 10, [1,2,2,6]).
204integer_weights_test(120, 60, [2,0]).
205
206:- end_tests('integer_weights/3').```
number_length(+Number:number, -Length:integer) is det
 An - integer representing a decimal number. Radix - An integer representing the radix used. Common values are `2.0` (binary), `8.0` (octal), `10.0` (decimal, used by number_length/2, and `16.0` (hexadecimal). Length - An integer representing the number of digits in the given number.
```  224number_length(M, L):-