1 % (c) 2020-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(kernel_reals, [construct_real/2, construct_negative_real/2,
6 construct_real_number/2,
7 is_real/1, is_real/2, is_real_wf/2,
8 is_float/1, is_float_wf/2,
9 is_not_real/1, is_not_float/1,
10 is_ground_real/1,
11 is_largest_positive_float/1, is_smallest_positive_float/1,
12 is_next_larger_float/2, is_next_smaller_float/2,
13 convert_int_to_real/2,
14 real_floor/2, real_ceiling/2,
15 real_addition_wf/4, real_subtraction_wf/4,
16 real_multiplication_wf/4, real_division_wf/5,
17 real_unary_minus_wf/3,
18 real_absolute_value_wf/3,
19 real_square_root_wf/4,
20 real_round_wf/3,
21 real_unop_wf/4, real_unop_wf/5,
22 real_binop_wf/6,
23 real_power_of_wf/5,
24 real_less_than_wf/3, real_less_than_equal_wf/3,
25 real_comp_wf/5,
26 real_maximum_of_set/4, real_minimum_of_set/4
27 ]).
28
29 % Reals/Floats in ProB are represented by terms of the form term(floating(Number))
30 % in future a proper wrapper such as real(Number) might be created
31
32 :- meta_predicate real_comp_wf(2,-,-,-,-).
33 :- meta_predicate real_comp_wf_aux(2,-,-,-,-).
34
35 :- use_module(module_information,[module_info/2]).
36 :- module_info(group,kernel).
37 :- module_info(description,'This module provides (external) functions to manipulate B reals and floats.').
38
39 :- use_module(error_manager).
40 :- use_module(self_check).
41 :- use_module(library(lists)).
42 :- use_module(debug).
43
44 :- use_module(kernel_objects,[exhaustive_kernel_check_wf/2,exhaustive_kernel_check_wf/3]).
45 :- use_module(extension('counter/counter'),
46 [next_smaller_abs_float/2, next_smaller_float/2,next_larger_float/2, next_float_twoards/3,
47 smallest_float/1, largest_float/1, smallest_abs_float/1]).
48
49 % used to construct a Value from real(Atom) AST node
50 construct_real(Atom,term(floating(Float))) :-
51 atom_codes(Atom,C),
52 number_codes(Nr,C),
53 Float is float(Nr). % make sure we store a float; not required for AST literals
54
55 construct_real_number(Atom,Nr) :-
56 atom_codes(Atom,C),
57 number_codes(Nr,C).
58
59 construct_negative_real(Atom,term(floating(Float))) :-
60 atom_codes(Atom,C),
61 number_codes(Nr,C),
62 Float is -float(Nr).
63
64 is_real_wf(X,_WF) :- is_real(X,_).
65 is_real(X) :- is_real(X,_).
66
67 is_real(term(floating(Nr)),Nr).
68 % TO DO: other formats
69
70 is_float_wf(X,_WF) :- is_float(X).
71
72 is_float(term(floating(_))).
73
74 is_not_real(_) :- fail.
75 is_not_float(_) :- fail.
76
77 is_ground_real(term(floating(Nr))) :- number(Nr).
78
79 is_largest_positive_float(term(floating(Nr))) :- largest_float(Nr).
80 is_smallest_positive_float(term(floating(Nr))) :- smallest_abs_float(Nr).
81
82 is_next_larger_float(term(floating(Nr)),term(floating(Next))) :-
83 block_next_larger_float(Nr,Next).
84 :- block block_next_larger_float(-,-).
85 block_next_larger_float(Nr,Next) :- nonvar(Nr),!,next_larger_float(Nr,Next).
86 block_next_larger_float(Nr,Next) :- next_smaller_float(Next,Nr).
87
88 is_next_smaller_float(term(floating(Nr)),term(floating(Next))) :-
89 block_next_larger_float(Next,Nr).
90
91
92 % convert an integer to a real, corresponds to the real(.) function in Atelier-B; convert_real as AST
93 convert_int_to_real(int(X),term(floating(R))) :-
94 convert_int_to_real_aux(X,R).
95
96 :- block convert_int_to_real_aux(-,-).
97 convert_int_to_real_aux(Int,Real) :- number(Int),!,
98 Real is float(Int).
99 convert_int_to_real_aux(Int,Real) :- Int1 is floor(Real),
100 Real is float(Int1),
101 Int1=Int.
102
103
104 % convert_int_floor as AST, Atelier-B floor(.)
105 real_floor(term(floating(R)),int(X)) :- real_floor_aux(R,X).
106 :- block real_floor_aux(-,?).
107 real_floor_aux(Real,Int) :- Int is floor(Real).
108
109 % convert_int_ceiling as AST, Atelier-B ceiling(.)
110 real_ceiling(term(floating(R)),int(X)) :- real_ceiling_aux(R,X).
111 :- block real_ceiling_aux(-,?).
112 real_ceiling_aux(Real,Int) :- Int is ceiling(Real).
113
114 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(1.0)),term(floating(2.0)),term(floating(3.0)),WF),WF)).
115 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(0.0)),term(floating(2.0)),term(floating(2.0)),WF),WF)).
116 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(-1.0)),term(floating(1.0)),term(floating(0.0)),WF),WF)).
117
118 real_addition_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
119 real_add_wf_aux(X,Y,R,WF).
120 :- block real_add_wf_aux(-,-,?,?), real_add_wf_aux(?,-,-,?), real_add_wf_aux(-,?,-,?).
121 real_add_wf_aux(X,Y,R,WF) :-
122 (nonvar(X)
123 -> (nonvar(Y)
124 -> safe_is(R,X+Y,WF)
125 ; allow_constraint_propagation(Kind),
126 safe_is(YY,R-X,WF),
127 confirm_solution_for_expr(YY,X+SOL,SOL,R,Kind,Solutions,WF)
128 -> set_solution_for_expr(Y,Solutions,WF)
129 ; real_add_wf_aux2(X,Y,R,WF) % delay until Y is known
130 )
131 ; allow_constraint_propagation(Kind),
132 safe_is(XX,R-Y,WF),
133 confirm_solution_for_expr(XX,SOL+Y,SOL,R,Kind,Solutions,WF)
134 -> set_solution_for_expr(X,Solutions,WF)
135 ; real_add_wf_aux2(X,Y,R,WF) % delay until X is known
136 ).
137
138 :- block real_add_wf_aux2(-,?,?,?), real_add_wf_aux2(?,-,?,?).
139 real_add_wf_aux2(X,Y,R,WF) :- safe_is(R,X+Y,WF).
140
141
142 % confirm_solution_for_expr(XX,ExprX,X,R : check if XX=X is a solution for R = ExprX, where ExprX contains variable X
143 % depending on solver setting : compute a list of candidate solutions around XX
144 confirm_solution_for_expr(XX,_,_,_,no_checking,Solutions,_) :- !, Solutions=XX.
145 confirm_solution_for_expr(XX,ExprX,X,R,simple_checking,Solutions,WF) :- !,
146 check_sol_direct(XX,ExprX,X,R,WF), Solutions=XX. % single solution
147 confirm_solution_for_expr(XX,ExprX,X,R,check_unique_solution,Solutions,WF) :- !,
148 check_sol_direct(XX,ExprX,X,R,WF), Solutions=XX, % single solution
149 % Now check if there are other solutions:
150 next_larger_float(XX,XN),
151 (X=XN,safe_is(R,ExprX,WF)
152 -> debug_format(19,'Reject ambiguous larger solution {~w,~w} for ~w = ~w~n',[XX,XN,ExprX,R]),fail
153 ; true
154 ),
155 next_smaller_float(XX,XP),
156 (X=XP,safe_is(R,ExprX,WF)
157 -> debug_format(19,'Reject ambiguous smaller solution {~w,~w} for ~w = ~w~n',[XX,XP,ExprX,R]),fail
158 ; true).
159 confirm_solution_for_expr(XX,ExprX,X,R,check_small_interval_solution,Interval,WF) :-
160 get_solutions_around(XX,ExprX,X,R,Interval,WF).
161
162 :- use_module(kernel_waitflags,[get_wait_flag/4]).
163 % set the solution based on result of confirm_solution_for_expr
164 set_solution_for_expr(X,XX,_WF) :- number(XX),!,X=XX. % deterministic constraint propagation found precise solution
165 set_solution_for_expr(X,[XX],_WF) :- !,X=XX. % ditto
166 set_solution_for_expr(_,[],_WF) :- !, fail. % no solutions
167 set_solution_for_expr(X,Interval,WF) :-
168 length(Interval,Len), % we found an interval list of possible solutions
169 get_wait_flag(Len,kernel_reals,WF,LWF),
170 set_sol_aux(X,Interval,LWF).
171 :- block set_sol_aux(-,?,-).
172 set_sol_aux(X,Interval,_) :-
173 ? member(X,Interval).
174
175
176 % get a sorted list of solutions around the candidate XX
177 get_solutions_around(XX,ExprX,X,R,Interval,WF) :-
178 (check_sol_direct(XX,ExprX,X,R,WF) % XX is a solution
179 -> get_preference(solver_strength,SS),
180 Limit is 10+SS, % number of solutions we are willing to find
181 next_larger_float(XX,XN),
182 find_larger_bounds(XN,Limit,ExprX,X,R,WF,LargerSols,[]),
183 next_smaller_float(XX,XP),
184 find_lower_bounds(XP,Limit,ExprX,X,R,WF,Interval,[XX|LargerSols])
185 ; (X='X',debug_format(19,'Reject non-solution ~w for ~w = ~w~n',[XX,ExprX,R]),fail ; true),
186 %next_smaller_float(XX,XP), (check_sol_direct(XP,ExprX,X,R,WF) -> write(xp(XP)),nl ; write(nxp(XP)),nl),
187 %next_larger_float(XX,XN), (check_sol_direct(XN,ExprX,X,R,WF) -> write(xn(XN)),nl ; write(nxp(XN)),nl),
188 Interval = [] % no floating point solutions; TODO: do we need to check smaller/larger values??
189 ).
190
191 % e.g. for x+1.0 = 3.0 we have 2.0 as solution and the previous float 1.9999999999999998, nothing else
192 % e.b. for x+2.0 = 3.0 we have four solutions {0.9999999999999998,0.9999999999999999,1.0,1.0000000000000002}
193 % card({x|x + 2.999 = 3.0}) = 2049
194 % card({x|x + 2.9999 = 3.0}) = 32769
195
196 % increment floats until we hit the first non-solution or we reach the limit
197 % TODO: try and compute this bound, rather then single-stepping
198 % TODO: enumerate the first solution and then generate an enum warning if we reach limit
199 find_larger_bounds(XP,Lim,ExprX,X,R,WF,[XP|Sols1],Sols2) :-
200 check_sol_direct(XP,ExprX,X,R,WF),!, Lim>0, L1 is Lim-1,
201 next_larger_float(XP,XP2),
202 find_larger_bounds(XP2,L1,ExprX,X,R,WF,Sols1,Sols2).
203 find_larger_bounds(_Bound,_Lim,_ExprX,_X,_R,_WF,Sols,Sols).
204
205 % decrement floats until we hit the first non-solution or we reach the limit
206 find_lower_bounds(XP,Lim,ExprX,X,R,WF,[XP|Sols1],Sols2) :-
207 check_sol_direct(XP,ExprX,X,R,WF),!, Lim>0, L1 is Lim-1,
208 next_smaller_float(XP,XP2),
209 find_lower_bounds(XP2,L1,ExprX,X,R,WF,Sols1,Sols2).
210 find_lower_bounds(_Bound,_Lim,_ExprX,_X,_R,_WF,Sols,Sols).
211
212 check_sol_direct(XX,ExprX,X,R,WF) :- !,
213 % (x + y = z <=> x = z - y) does not hold for all floats, e.g., when x very small and y very large (x+y)-y = 0
214 (\+((X=XX,safe_is(R,ExprX,WF))) -> fail ; true).
215
216
217 :- use_module(probsrc(preferences), [get_preference/2]).
218
219 allow_constraint_propagation(Kind) :-
220 get_preference(solver_for_reals,Solver),
221 get_solver_kind(Solver,Kind).
222
223 get_solver_kind(aggressive_float_solver,no_checking). % like CLP(R): do not check propagated solution
224 get_solver_kind(float_solver,simple_checking). % check propagated solution, but do not check uniqueness
225 %get_solver_kind(precise_float_solver,check_unique_solution).
226 get_solver_kind(precise_float_solver,check_small_interval_solution). % perform limited constraint propagation, fail directly if no solution exists
227
228 % we could have various other options:
229 % aggressive: do it like CLP(R), which can generate non-solutions
230 % | ?- {X+10.0e10 = 1.0e-9}, write(sol(X)),nl, {X+10.0e10 = 1.0e-9}.
231 % prints sol(-1.0E+11) but then fails
232 % conservative: do the precision check and also check that there are no other solutions (not done yet)
233 % e.g., X+10000000000.0 = 10000000000.0 & (X=0.000000000001 or X=2.0) is FALSE with the float_solver, but TRUE with none
234
235
236 :- assert_must_succeed(exhaustive_kernel_check_wf(kernel_reals:real_subtraction_wf(term(floating(3.0)),term(floating(2.0)),term(floating(1.0)),WF),WF)).
237 real_subtraction_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
238 real_sub_wf_aux(X,Y,R,WF).
239 :- block real_sub_wf_aux(-,-,?,?), real_sub_wf_aux(?,-,-,?), real_sub_wf_aux(-,?,-,?).
240 real_sub_wf_aux(X,Y,R,WF) :-
241 (nonvar(X)
242 -> (nonvar(Y)
243 -> safe_is(R,X-Y,WF)
244 ; allow_constraint_propagation(Kind),
245 safe_is(YY,X-R,WF),
246 confirm_solution_for_expr(YY,X-SOL,SOL,R,Kind,Solutions,WF)
247 -> set_solution_for_expr(Y,Solutions,WF) % deterministic constraint propagation found precise solution
248 ; real_sub_wf_aux2(X,Y,R,WF) % delay until Y is known
249 )
250 ; allow_constraint_propagation(Kind),
251 safe_is(XX,R+Y,WF),
252 confirm_solution_for_expr(XX,SOL-Y,SOL,R,Kind,Solutions,WF)
253 -> set_solution_for_expr(X,Solutions,WF) % deterministic constraint propagation found precise solution
254 ; real_sub_wf_aux2(X,Y,R,WF) % delay until X is known
255 ).
256
257 :- block real_sub_wf_aux2(-,?,?,?), real_sub_wf_aux2(?,-,?,?).
258 real_sub_wf_aux2(X,Y,R,WF) :- safe_is(R,X-Y,WF).
259
260
261 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_multiplication_wf(term(floating(3.0)),term(floating(2.0)),term(floating(6.0)),WF),WF)).
262 real_multiplication_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
263 real_mul_wf_aux0(X,Y,R,WF).
264
265 :- block real_mul_wf_aux0(-,-,-,?).
266 real_mul_wf_aux0(X,Y,R,_WF) :- (X==0.0 ; Y==0.0), !, R=0.0.
267 real_mul_wf_aux0(X,Y,R,WF) :- var(X),X==Y, !, % X*X = R -> compute sqrt
268 kernel_square_inverse(X,R,WF).
269 real_mul_wf_aux0(X,Y,R,WF) :- real_mul_wf_aux(X,Y,R,WF).
270
271 :- block real_mul_wf_aux(-,-,?,?), real_mul_wf_aux(?,-,-,?), real_mul_wf_aux(-,?,-,?).
272 real_mul_wf_aux(X,Y,R,_WF) :- (X==0.0 ; Y==0.0), !, R=0.0.
273 real_mul_wf_aux(X,Y,R,WF) :-
274 (nonvar(X)
275 -> (nonvar(Y)
276 -> safe_is(R,X*Y,WF)
277 ; allow_constraint_propagation(Kind),
278 safe_is(YY,R/X,WF),
279 confirm_solution_for_expr(YY,X*SOL,SOL,R,Kind,Solutions,WF)
280 -> set_solution_for_expr(Y,Solutions,WF)
281 ; real_mul_wf_aux2(X,Y,R,WF) % delay until Y is known
282 )
283 ; allow_constraint_propagation(Kind),
284 safe_is(XX,R/Y,WF),
285 confirm_solution_for_expr(XX,SOL*Y,SOL,R,Kind,Solutions,WF)
286 -> set_solution_for_expr(X,Solutions,WF)
287 ; real_mul_wf_aux2(X,Y,R,WF) % delay until X is known
288 ).
289
290 :- block real_mul_wf_aux2(-,?,?,?), real_mul_wf_aux2(?,-,?,?).
291 real_mul_wf_aux2(X,Y,R,WF) :- safe_is(R,X*Y,WF).
292
293 % compute sqrt, R is known
294 kernel_square_inverse(X,R,WF) :-
295 (R < 0.0 -> fail
296 ; allow_constraint_propagation(Kind),
297 safe_is(XX,sqrt(R),WF),
298 confirm_solution_for_expr(XX,SOL*SOL,SOL,R,Kind,SolutionsPos,WF),
299 (XX=0.0 -> Solutions = SolutionsPos
300 ; NX is -XX,
301 confirm_solution_for_expr(NX,SOL*SOL,SOL,R,Kind,SolutionsNeg,WF),
302 append(SolutionsPos,SolutionsNeg,Solutions)
303 )
304 -> set_solution_for_expr(X,Solutions,WF)
305 ; real_mul_wf_aux(X,X,R,WF)).
306
307
308 :- block 'real_square_root_wf'(-,-,?,?).
309 real_square_root_wf(term(floating(X)),term(floating(R)),Span,WF) :-
310 real_sqrt_wf_aux(X,R,Span,WF).
311 :- block real_sqrt_wf_aux(-,-,?,?).
312 real_sqrt_wf_aux(X,R,_Span,WF) :- var(X),
313 allow_constraint_propagation(Kind),
314 get_preference(find_abort_values,false),
315 (R >= 0.0
316 -> safe_is(XX,R*R,WF),
317 confirm_solution_for_expr(XX,sqrt(SOL),SOL,R,Kind,Solutions,WF)
318 ; Solutions = []), % RSQRT always returns a positive number
319 !,
320 set_solution_for_expr(X,Solutions,WF).
321 real_sqrt_wf_aux(X,R,Span,WF) :-
322 real_unop_wf_aux('sqrt',X,R,Span,WF).
323
324 :- assert_must_succeed(exhaustive_kernel_check_wf(kernel_reals:real_division_wf(term(floating(6.0)),term(floating(2.0)),term(floating(3.0)),unknown,WF),WF)).
325 real_division_wf(term(floating(X)),term(floating(Y)),term(floating(R)),Span,WF) :-
326 real_div_wf_aux(X,Y,R,Span,WF).
327 :- block real_div_wf_aux(-,?,-,?,?), real_div_wf_aux(?,-,?,?,?).
328 real_div_wf_aux(X,Y,R,Span,WF) :- nonvar(X),!,safe_is(R,X/Y,Span,WF).
329 real_div_wf_aux(X,Y,R,Span,WF) :- Y \= 0.0, allow_constraint_propagation(Kind),
330 safe_is(XX,R*Y,Span,WF),
331 confirm_solution_for_expr(XX,SOL/Y,SOL,R,Kind,Solutions,WF),
332 !,
333 set_solution_for_expr(X,Solutions,WF).
334 real_div_wf_aux(X,Y,R,Span,WF) :- when(nonvar(X),safe_is(R,X/Y,Span,WF)).
335
336 :- block 'real_unary_minus_wf'(-,-,?).
337 real_unary_minus_wf(term(floating(X)),term(floating(R)),WF) :-
338 real_um_wf_aux(X,R,WF).
339 :- block real_um_wf_aux(-,-,?).
340 real_um_wf_aux(X,R,_) :- nonvar(X),!,R is -X.
341 real_um_wf_aux(X,R,_) :- X is -R.
342
343 :- block 'real_absolute_value_wf'(-,-,?).
344 real_absolute_value_wf(term(floating(X)),term(floating(R)),WF) :-
345 real_abs_wf_aux(X,R,WF).
346 :- block real_abs_wf_aux(-,-,?).
347 real_abs_wf_aux(X,R,_) :- nonvar(X),!, R is abs(X).
348 real_abs_wf_aux(X,R,_) :- R = 0.0, !, X=0.0.
349 real_abs_wf_aux(_,R,_) :- R < 0.0, !, fail.
350 real_abs_wf_aux(X,R,WF) :- MR is -R,
351 Solutions = [R,MR],
352 set_solution_for_expr(X,Solutions,WF).
353
354 real_round_wf(term(floating(X)),int(R),_WF) :-
355 when(nonvar(X), R is round(X)).
356
357 :- use_module(kernel_waitflags,[add_wd_error/3, add_wd_error_span/4]).
358 % a version of is/2 which catches overflows
359 safe_is(R,Expr,WF) :-
360 safe_is(R,Expr,unknown,WF).
361 safe_is(R,Expr,Span,WF) :-
362 catch(R is Expr,
363 error(evaluation_error(ERR),_),
364 process_evaluation_error(ERR,Expr,Span,WF)).
365
366 process_evaluation_error(float_overflow,Expr,Span,WF) :- !,
367 add_wd_error_span('Float Overflow while computing:',Expr,Span,WF).
368 process_evaluation_error(zero_divisor,Expr,Span,WF) :- !,
369 add_wd_error_span('Division by zero while computing:',Expr,Span,WF).
370 process_evaluation_error(undefined,Expr,Span,WF) :- !,
371 add_wd_error_span('Arithmetic operator undefined while computing:',Expr,Span,WF).
372 process_evaluation_error(_,Expr,Span,WF) :-
373 add_wd_error_span('Unknown evaluation error while computing:',Expr,Span,WF).
374
375
376 % call a Prolog unary artihmetic operator
377 real_unop_wf(OP,X,R,WF) :-
378 real_unop_wf(OP,X,R,unknown,WF).
379
380 real_unop_wf(OP,RX,RR,Span,WF) :-
381 get_real(RX,X,OP,Span,WF), get_real(RR,R,OP,Span,WF),
382 real_unop_wf_aux(OP,X,R,Span,WF).
383 :- block real_unop_wf_aux(-,?,?,?,?), real_unop_wf_aux(?,-,?,?,?).
384 real_unop_wf_aux(OP,X,R,Span,WF) :-
385 Expr =.. [OP,X],
386 safe_is(R,Expr,Span,WF).
387
388 :- use_module(probsrc(tools_strings),[ajoin/2]).
389 :- use_module(kernel_waitflags,[add_error_wf/5]).
390 get_real(Var,Real,_,_,_) :- var(Var),!, Var=term(floating(Real)).
391 get_real(term(F),Real,_,_,_) :- !, F=floating(Real).
392 get_real(Other,_,OP,Span,WF) :-
393 ajoin(['Argument for ',OP,' is not a real number:'],Msg),
394 add_error_wf(kernel_reals,Msg,Other,Span,WF).
395
396 % when called for ** operator the exponent Y is a natural number represented as a float; for RPOW Y can be any float
397 real_power_of_wf(RX,RY,RR,Span,WF) :-
398 get_real(RX,X,'exp',Span,WF), get_real(RY,Y,'exp',Span,WF), get_real(RR,R,'exp',Span,WF),
399 real_power_of_aux(X,Y,R,Span,WF).
400
401 :- block real_power_of_aux(-,-,?,?,?), real_power_of_aux(?,-,-,?,?), real_power_of_aux(-,?,-,?,?).
402 % we currently require to know at least the exponent, TODO: in future we could also compute the log
403 real_power_of_aux(X,Y,R,Span,WF) :-
404 var(X),!,
405 real_power_inverse(X,Y,R,Span,WF).
406 real_power_of_aux(X,Y,Res,Span,WF) :-
407 real_power_direct(X,Y,Res,Span,WF).
408
409 % X is variable, Y and R are known
410 real_power_inverse(X,Y,R,_Span,WF) :- Y=2.0,!,
411 kernel_square_inverse(X,R,WF).
412 real_power_inverse(X,Y,R,_Span,_WF) :- Y=1.0,!, X=R.
413 real_power_inverse(X,Y,R,Span,WF) :-
414 real_power_direct(X,Y,R,Span,WF).
415 % TODO: add more exponents and compute root
416
417 :- block real_power_direct(-,?,?,?,?), real_power_direct(?,-,?,?,?).
418 real_power_direct(X,Y,R,Span,WF) :-
419 safe_is(R,exp(X,Y),Span,WF).
420
421 % call a Prolog binary artihmetic operator
422 real_binop_wf(OP,RX,RY,RR,Span,WF) :-
423 get_real(RX,X,OP,Span,WF), get_real(RY,Y,OP,Span,WF), get_real(RR,R,OP,Span,WF),
424 real_binop_wf_aux(OP,X,Y,R,Span,WF).
425 :- block real_binop_wf_aux(-,?,?,?,?,?), real_binop_wf_aux(?,-,?,?,?,?), real_binop_wf_aux(?,?,-,?,?,?).
426 real_binop_wf_aux(OP,X,Y,R,Span,WF) :-
427 Expr =.. [OP,X,Y],
428 safe_is(R,Expr,Span,WF).
429
430 real_less_than_wf(X,Y,WF) :- real_comp_wf('<',X,Y,pred_true,WF).
431 real_less_than_equal_wf(X,Y,WF) :- real_comp_wf('=<',X,Y,pred_true,WF).
432
433
434 % call a Prolog bianry artihmetic operator
435 real_comp_wf(OP,term(floating(X)),term(floating(Y)),R,WF) :-
436 real_comp_wf_aux(OP,X,Y,R,WF).
437 :- block real_comp_wf_aux(-,?,?,?,?), real_comp_wf_aux(?,-,?,?,?), real_comp_wf_aux(?,?,-,?,?).
438 real_comp_wf_aux(OP,X,Y,R,_) :-
439 (call(OP,X,Y) -> R=pred_true ; R=pred_false).
440
441 % -----------------
442
443 :- use_module(probsrc(kernel_tools),[ground_value_check/2]).
444 real_maximum_of_set(Set,Res,Span,WF) :-
445 ground_value_check(Set,Gr),
446 rmax_set(Gr,Set,Res,Span,WF).
447
448 :- block rmax_set(-,?,?,?,?).
449 rmax_set(_,Set,Res,Span,WF) :-
450 custom_explicit_sets:expand_and_convert_to_avl_set(Set,ESet,'RMAXIMUM','RMAXIMUM'),
451 (ESet=empty
452 -> add_wd_error_span('max applied to empty set of reals:',Set,Span,WF)
453 ; custom_explicit_sets:max_of_explicit_set_wf(avl_set(ESet),Res,WF)
454 ).
455
456 real_minimum_of_set(Set,Res,Span,WF) :-
457 ground_value_check(Set,Gr),
458 rmin_set(Gr,Set,Res,Span,WF).
459
460 :- block rmin_set(-,?,?,?,?).
461 rmin_set(_,Set,Res,Span,WF) :-
462 custom_explicit_sets:expand_and_convert_to_avl_set(Set,ESet,'RMINIMUM','RMINIMUM'),
463 (ESet=empty
464 -> add_wd_error_span('min applied to empty set of reals:',Set,Span,WF)
465 ; custom_explicit_sets:min_of_explicit_set_wf(avl_set(ESet),Res,WF)
466 ).
467
468
469 % not used yet: useful for kernel_strings ?
470 %:- block real_to_string(-,?).
471 %real_to_string(term(floating(I)),S) :- real_to_string2(I,S).
472 %
473 %:- block real_to_string2(-,?).
474 %real_to_string2(Num,Res) :-
475 % number_codes(Num,C),
476 % atom_codes(S,C), Res=string(S).
477