1 #include <FPG/Static_filter_with_scaling.h>
2 #include <FPG/Misc_visitors.h>
3 #include <FPG/Abstract_interpretation_visitor.h>
4 #include <FPG/Prettyprint_visitor.h>
5 #include <FPG/Transformation.h>
6 #include <FPG/Error_bound_value.h>
7 #include <FPG/Static_filter_error.h>
8 #include <FPG/Group_index_value.h>
9 #include <CGALmini/basic.h>
10 
11 #include <cassert>
12 #include <vector>
13 #include <set>
14 #include <algorithm>
15 #include <cmath>
16 #include <limits>
17 #include <sstream>
18 
19 extern "C" {
20 #include <fenv.h>
21 }
22 
23 //#define DEBUG 0
24 #include <FPG/MSG.h>
25 
26 
27 using namespace Static_filter_with_scaling;
28 
29 struct Contains_translation_on_fresh_values :
30     public Attribute_visitor<bool>
31 {
32     Abstract_interpretation_visitor *absint_is_fresh;
33 
34     // sfe knows, which AST::Expression*s are fresh
Contains_translation_on_fresh_valuesContains_translation_on_fresh_values35     Contains_translation_on_fresh_values( Abstract_interpretation_visitor *absint_is_fresh )
36         : /* inter-procedural, */
37           absint_is_fresh(absint_is_fresh)
38     {
39         set_default_value( false );
40         set_shortcut_value( true );
41     }
42 
combineContains_translation_on_fresh_values43     virtual bool combine( bool a, bool b ) {
44         return a || b;
45     }
46 
visitContains_translation_on_fresh_values47     virtual void visit( AST::BinaryExpression* bexp ) {
48         Error_bound_value *is_fresh1 = dynamic_cast<Error_bound_value*>( absint_is_fresh->get_analysis_result( bexp->e1 ) );
49         Error_bound_value *is_fresh2 = dynamic_cast<Error_bound_value*>( absint_is_fresh->get_analysis_result( bexp->e2 ) );
50         assert( is_fresh1 != nullptr && is_fresh2 != nullptr );
51         if( is_fresh1->is_fresh() && is_fresh2->is_fresh() && bexp->kind == AST::BinaryExpression::SUB ) {
52             MSG("")
53             //bexp->dump(0);
54             result_value = true;
55         } else
56             Attribute_visitor<bool>::visit( bexp );
57     }
58 };
59 
60 struct Contains_float_compare_on_derived_values :
61     public Attribute_visitor<bool>
62 {
63     Abstract_interpretation_visitor *absint_is_fresh;
64 
65     // sfe knows, which AST::Expression*s are fresh
Contains_float_compare_on_derived_valuesContains_float_compare_on_derived_values66     Contains_float_compare_on_derived_values( Abstract_interpretation_visitor *absint_is_fresh )
67         : /* inter-procedural, */
68           absint_is_fresh(absint_is_fresh)
69     {
70         set_default_value( false );
71         set_shortcut_value( true );
72     }
73 
combineContains_float_compare_on_derived_values74     virtual bool combine( bool a, bool b ) {
75         return a || b;
76     }
77 
visitContains_float_compare_on_derived_values78     virtual void visit( AST::BinaryExpression* bexp ) {
79         if( is_float( bexp->e1->getType() ) ||
80             is_float( bexp->e2->getType() ) )
81         {
82             Error_bound_value *is_fresh1 = dynamic_cast<Error_bound_value*>( absint_is_fresh->get_analysis_result( bexp->e1 ) );
83             Error_bound_value *is_fresh2 = dynamic_cast<Error_bound_value*>( absint_is_fresh->get_analysis_result( bexp->e2 ) );
84             assert( is_fresh1 != nullptr && is_fresh2 != nullptr );
85             bool is_fresh = is_fresh1->is_fresh() && is_fresh2->is_fresh();
86             //MSG(is_fresh)
87             switch( bexp->kind ) {
88             case AST::BinaryExpression::EQ:
89             case AST::BinaryExpression::NEQ:
90             case AST::BinaryExpression::GEQ:
91    	    case AST::BinaryExpression::LEQ:
92                 if( !is_fresh )
93                     throw RuntimeError("no equality compare for derived values!", bexp->location );
94 		// fallthrough
95             case AST::BinaryExpression::LE:
96             case AST::BinaryExpression::GR:
97                 if( !is_fresh ) {
98                     result_value = true;
99                     return;
100                 }
101                 break;
102             default: break;
103             }
104         }
105         Attribute_visitor<bool>::visit( bexp );
106     }
107 
visitContains_float_compare_on_derived_values108     virtual void visit( AST::UnaryFunction *uf ) {
109         Error_bound_value *is_fresh = dynamic_cast<Error_bound_value*>( absint_is_fresh->get_analysis_result( uf->e ) );
110         assert( is_fresh != nullptr );
111         if( uf->kind == AST::UnaryFunction::XSIGN && !is_fresh->is_fresh() )
112             result_value = true;
113         else
114             Attribute_visitor<bool>::visit( uf );
115     }
116 };
117 
118 
119 
120 struct Subst_funcall_filter : Expression_filter {
121     Add_bound_variables *add_bounds;
122 
Subst_funcall_filterSubst_funcall_filter123     Subst_funcall_filter( Add_bound_variables *add_bounds )
124         : add_bounds(add_bounds)
125     {}
126 
operator ()Subst_funcall_filter127     virtual bool operator()( AST::Expression *e ) {
128         // this is not really efficient ....
129         MSG("")
130         //e->dump(0);
131         AST::FunctionCall *funcall = dynamic_cast<AST::FunctionCall*>(e);
132         assert( funcall != nullptr );
133         if( funcall->fun_type->is_extern )
134             return false;
135         assert( funcall->called_function != nullptr );
136         Contains_translation_on_fresh_values       has_fresh_sub ( add_bounds->absint_is_fresh );
137         Contains_float_compare_on_derived_values   has_float_comp( add_bounds->absint_is_fresh );
138         funcall->called_function->accept( &has_fresh_sub );
139         funcall->called_function->accept( &has_float_comp );
140         //std::cout << has_fresh_sub.result_value << has_float_comp.result_value << std::endl;
141         return has_fresh_sub.result_value || has_float_comp.result_value;
142     }
143 };
144 
145 // for each group g, check if another group h
146 // is a subset/is equal to g. store this subset/dependency relation
147 void
make_partial_order()148 Group_cache::make_partial_order() {
149     subset_relation.clear();
150     for( iterator g_it = begin(); g_it!= end(); ++g_it ) {
151         const Group &g = g_it->first;
152         Variable   *vg = g_it->second;
153         /*std::cout << "group g:" << vg->id << " with: ";
154         for( Group::iterator xx_it = g.begin(); xx_it != g.end(); ++xx_it )
155             std::cout << (*xx_it)->id << " ";
156         std::cout << std::endl;*/
157         for( iterator h_it = begin(); h_it!= end(); ++h_it ) {
158             if( g_it == h_it )
159                 continue;
160             const Group &h = h_it->first;
161             Variable   *vh = h_it->second;
162             /*std::cout << "checking group " << vh->id << " with: ";
163             for( Group::iterator xx_it = h.begin(); xx_it != h.end(); ++xx_it )
164                 std::cout << (*xx_it)->id << " ";
165             std::cout << std::endl;*/
166 
167             // check if h is a subset of g (or equal to g)
168             if( std::includes( g.begin(), g.end(), h.begin(), h.end() ) ) {
169                 // the variable vh can only appear once, so no need to check for
170                 // duplicates
171                 subset_relation[vg].insert( vh );
172                 //std::cout << " ++ var " << vh->id << " is subset" << std::endl;
173             } //else
174                 //std::cout << " -- var " << vh->id << " no subset" << std::endl;
175         }
176     }
177 }
178 
179 bool
is_intersection_free(Variable * maxvar)180 Group_cache::is_intersection_free( Variable *maxvar /*, std::vector< Variable* >& other_max_vars */ ) {
181     Reverse_mapping::iterator g_it = reverse_mapping.find( maxvar );
182     assert( g_it != reverse_mapping.end() );
183     const Group &g = g_it->second;
184     for( iterator h_it = begin(); h_it!= end(); ++h_it ) {
185         const Group &h = h_it->first;
186         if( g == h )
187             continue;
188         std::set< Variable* > intersection;
189         std::set_intersection( g.begin(), g.end(), h.begin(), h.end(),
190                                std::inserter( intersection, intersection.begin() ) );
191         if( intersection.empty() )
192             continue;
193         //bool g_includes_h = std::includes( g.begin(), g.end(), h.begin(), h.end() );
194         //bool h_includes_g = std::includes( h.begin(), h.end(), g.begin(), g.end() );
195         bool g_includes_h = subset_relation[g_it->first].find( h_it->second ) != subset_relation[g_it->first].end();
196         bool h_includes_g = subset_relation[h_it->second].find( g_it->first ) != subset_relation[h_it->second].end();
197         if( ! g_includes_h && ! h_includes_g ) {
198             /*std::set< Variable* >::iterator it;
199             std::cout << "g: ";
200             for( it = g.begin(); it != g.end(); ++ it )
201                 std::cout << (*it)->id << " ";
202             std::cout << std::endl << "h: ";
203             for( it = h.begin(); it != h.end(); ++ it )
204                 std::cout << (*it)->id << " ";
205             std::cout << std::endl;*/
206             return false;
207         }
208     }
209     return true;
210 }
211 
212 bool
is_maximum(Variable * maxvar,std::vector<Variable * > & other_max_vars)213 Group_cache::is_maximum( Variable* maxvar, std::vector< Variable* >& other_max_vars ) {
214     std::vector< Variable* >::iterator begin = other_max_vars.begin(),
215                                        end   = other_max_vars.end();
216     for( ; begin != end; ++begin ) {
217         if( maxvar == *begin )
218             continue;
219         //std::cout << "varname: " << (*begin)->id << std::endl;
220         if( subset_relation[maxvar].find( *begin ) == subset_relation[maxvar].end() )
221             return false;
222     }
223     return true;
224 }
225 
226 unsigned int
number_of_subsets(Variable * maxvar,std::vector<Variable * > & max_vars,std::set<Variable * > & ignore)227 Group_cache::number_of_subsets( Variable* maxvar, std::vector< Variable* >& max_vars, std::set< Variable*> &ignore ) {
228     std::vector< Variable* >::iterator begin = max_vars.begin(),
229                                        end   = max_vars.end();
230     unsigned int counter = 0;
231     for( ; begin != end; ++begin ) {
232         if( maxvar == *begin )
233             continue;
234         //std::cout << "varname: " << (*begin)->id << std::endl;
235         if( subset_relation[maxvar].find( *begin ) != subset_relation[maxvar].end() ) {
236             ignore.insert( *begin );
237             ++counter;
238         }
239     }
240     return counter;
241 }
242 
243 Variable*
find_maximum(std::vector<Variable * > & max_vars,std::set<Variable * > & ignore)244 Group_cache::find_maximum( std::vector< Variable* >& max_vars, std::set< Variable*> &ignore ) {
245     std::vector< Variable* >::iterator begin = max_vars.begin(),
246                                        end   = max_vars.end();
247     Variable *result = nullptr;
248     std::set< Variable*> ignore_tmp;
249     unsigned int max_subsets = 0;
250     for( ; begin != end; ++begin ) {
251         //std::cout << "varname: " << (*begin)->id << std::endl;
252         ignore_tmp.clear();
253         unsigned int n = number_of_subsets( *begin, max_vars, ignore_tmp );
254         if( n > max_subsets ) {
255             result = *begin;
256             // not terribly efficient ...
257             ignore = ignore_tmp;
258             max_subsets = n;
259         }
260     }
261     return result;
262 }
263 
264 
265 // ----------------------
266 struct Use_as_max_value : public Abstract_value {
Use_as_max_valueUse_as_max_value267     Use_as_max_value(bool is_fresh   = true,
268                      bool use_as_max = false)
269         : is_fresh_(is_fresh),
270           use_as_max_(use_as_max)
271     {}
272 
273     Use_as_max_value*
downcastUse_as_max_value274     downcast( Abstract_value* value ) {
275         Use_as_max_value *v= dynamic_cast<Use_as_max_value*>( value );
276         assert( v!= nullptr );
277         return v;
278     }
279     virtual Use_as_max_value*
get_initial_valueUse_as_max_value280     get_initial_value( Variable *var ) { argused(var); return new Use_as_max_value( true, true ); }
281 
282     virtual Use_as_max_value*
addUse_as_max_value283     add( Abstract_value* other ) {
284         if( downcast(other)->is_fresh() && is_fresh() ) {
285             downcast(other)->use_as_max_ = false;
286             use_as_max_ = false;
287             return new Use_as_max_value( false, true );
288         } /*else {
289             if( downcast(other)->is_fresh() ) {
290                 assert(!is_fresh());
291                 downcast(other)->use_as_max_ = true;
292             } else if( is_fresh() ) {
293                 assert(!downcast(other)->is_fresh());
294                 use_as_max_ = true;
295             }
296         }*/
297         return new Use_as_max_value(false,false);
298     }
299 
300     virtual Use_as_max_value*
subUse_as_max_value301     sub( Abstract_value* other ) {
302         return add( other );
303     }
304 
305 
306     virtual Use_as_max_value*
mulUse_as_max_value307     mul( Abstract_value* other ) {
308         argused(other);
309         return new Use_as_max_value(false,false);
310     }
311 
312     //virtual void funcall( AST::Expression* funcall) { if( is_fresh() ) use_as_max_ = true; }
313 
divUse_as_max_value314     virtual Use_as_max_value* div( Abstract_value* other )  { argused(other); CGAL_error(); return nullptr; }
sqrtUse_as_max_value315     virtual Use_as_max_value* sqrt()                        { CGAL_error(); return nullptr; }
joinUse_as_max_value316     virtual Use_as_max_value* join( Abstract_value* other ) { argused(other); return this; }
317 
cloneUse_as_max_value318     virtual Use_as_max_value* clone() { return new Use_as_max_value(*this); }
319     // returns true if it is equivalent to a default constructed abstract value
is_freshUse_as_max_value320     virtual bool is_fresh() { return is_fresh_; }
321 
dumpUse_as_max_value322     virtual std::ostream& dump( std::ostream& out ) {
323         out << "use as max: " << use_as_max_;
324         return out;
325     }
326 
327     bool      is_fresh_;
328     bool      use_as_max_;
329 };
330 
331 // --------------------------------------------------------------
332 
333 struct CSE_max
334     : public Common_subexpression_elimination
335 {
CSE_maxCSE_max336     CSE_max( Add_bound_variables *add_bounds ) : add_bounds(add_bounds) {}
337 
filterCSE_max338     virtual bool filter( AST::BinaryExpression *e ) {
339         return add_bounds->use_as_max( e );
340     }
341 
visitCSE_max342     virtual void visit( AST::AssignmentExpression *e ) {
343         if( add_bounds->use_as_max( e ) )
344             Transformation_visitor::push( e );
345         else
346             Transformation_visitor::visit( e );
347     }
348 
updateCSE_max349     virtual void update( AST::Node *node_old, AST::Node *node_new ) {
350         Transformation_visitor::update( node_old, node_new );
351         AST::Expression *e_old = dynamic_cast< AST::Expression* >(node_old);
352         AST::Expression *e_new = dynamic_cast< AST::Expression* >(node_new);
353         if( e_old != nullptr )
354             assert( e_new != nullptr );
355         add_bounds->absint_max->update( e_old, e_new );
356     }
357 
358     Add_bound_variables *add_bounds;
359 };
360 
361 struct Predicate_use_as_max : public Expression_filter {
Predicate_use_as_maxPredicate_use_as_max362     Predicate_use_as_max( Add_bound_variables *add_bounds ) : add_bounds(add_bounds) {}
operator ()Predicate_use_as_max363     virtual bool operator()( AST::Expression *e ) {
364         return add_bounds->use_as_max( e );
365     }
366     Add_bound_variables *add_bounds;
367 };
368 
369 
Add_bound_variables(bool use_aposteriori_check)370 Add_bound_variables::Add_bound_variables( bool use_aposteriori_check )
371    : epsilon_tmp_variable(nullptr),
372      absint_max(new Abstract_interpretation_visitor( new Use_as_max_value )),
373      max_var_counter(0),
374      use_aposteriori_check(use_aposteriori_check)
375 {
376     Expression_filter *f = new Predicate_use_as_max( this );
377 
378     Abstract_value *sfe = new Static_filter_error( f );
379     absint_sfe = new Abstract_interpretation_visitor( sfe );
380 
381     Abstract_value *giv = new Group_index_value( f );
382     absint_giv = new Abstract_interpretation_visitor( giv );
383 
384     Abstract_value *is_fresh = new Error_bound_value();
385     absint_is_fresh = new Abstract_interpretation_visitor( is_fresh );
386 }
387 
388 
389 bool
use_as_max(AST::Expression * e)390 Add_bound_variables::use_as_max( AST::Expression* e) {
391     MSG("")
392     Use_as_max_value *uamv= dynamic_cast< Use_as_max_value* >( absint_max->get_analysis_result( e ) );
393     assert( uamv != nullptr );
394     return uamv->use_as_max_ ;
395 }
396 
397 
398 
399 static bool
find_max_input(AST::FunctionDefinition * fundef,double & max_input)400 find_max_input( AST::FunctionDefinition *fundef, double &max_input ) {
401     max_input = std::numeric_limits<double>::max();
402     Abstract_interpretation_visitor
403         *absint = new Abstract_interpretation_visitor(
404                       new Static_filter_error(new Expression_filter)
405                   );
406     unsigned int phase = 0;
407     unsigned int counter = 0;
408     do {
409         ::feclearexcept( FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID );
410         //std::cout << "trying with max_input = " << max_input << " .... " << std::endl;
411         absint->clear( new Static_filter_error( new Expression_filter, CGAL::Static_filter_error(max_input) ) );
412         absint->visit( fundef );
413         bool overflow = ::fetestexcept ( FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID );
414         if(  overflow == (phase % 2) ) {
415             ++phase;
416             //std::cout << "next phase: " << phase << std::endl;
417         }
418         if( max_input < 100.0 )
419             return false;
420         switch( phase ) {
421         case 0  : max_input  = std::sqrt( max_input ); break;
422         case 1  : max_input *= std::sqrt( max_input ); break;
423         default : max_input /= 2.0; break;
424         }
425         if( ++counter > 1000000000 )
426             return false;
427     } while( phase <= 2 );
428     //std::cout << "success with max_input = " << max_input << std::endl;
429     delete absint;
430     return true;
431 }
432 
433 void
visit(AST::TranslationUnit * tu)434 Add_bound_variables::visit( AST::TranslationUnit* tu) {
435     Compute_call_count                       compute_call_count;
436     Rewrite_float_comparisons                rewrite_float_comp( this );
437     Rewrite_sign_of_fresh_values             rewrite_fresh_sign( this );
438     Contains_float_compare_on_derived_values has_float_comp( absint_is_fresh );
439     Subst_funcall_filter                     subst_filter( this );
440     Substitute_funcalls                      subst_funcalls( &subst_filter );
441     AST::ListOfFunctions::iterator           it;
442     AST::ListOfFunctions::reverse_iterator   r_it;
443     subst_funcalls.collect_variables = collect_variables;
444 
445     for( r_it = tu->functions.rbegin(); r_it != tu->functions.rend(); ++r_it ) {
446         collect_variables->clear();
447         absint_is_fresh->clear();
448         has_float_comp.reset();
449         AST::FunctionDefinition *fundef = *r_it;
450         if( !fundef->type->is_extern ) {
451             fundef->accept( collect_variables );
452             fundef->accept( absint_is_fresh );
453             fundef->accept( &has_float_comp );
454             if( !has_float_comp.result_value )
455                 continue;
456             //std::cerr << "substituting function calls in " << fundef->type->id << std::endl;
457             // subst'ion works one funcall at a time, starting from the "outside":
458             // function calls that are made in the called function have to be replaced
459             // subsequently. repeat until no change has been made
460             //Prettyprint_visitor pretty( std::cout );
461             //fundef->accept( &pretty );
462             do {
463                 subst_funcalls.is_dirty = false;
464                 fundef->accept( &subst_funcalls );
465                 fundef->accept( absint_is_fresh );
466                 /*if( subst_funcalls.is_dirty ) {
467                     std::cerr << " +++ iteration: " << std::endl;
468                     fundef->accept( &pretty );
469                 }*/
470             } while( subst_funcalls.is_dirty );
471             //std::cerr << "finished substituting. rewriting fresh sign expressions:" << std::endl;
472             fundef->accept( &rewrite_fresh_sign );
473             //fundef->accept( &pretty );
474         }
475     }
476 
477     // here, each function only contains function calls without
478     // derived float comparisons. this allows to compute max groups
479     // in only that toplevel function
480     for( it = tu->functions.begin(); it != tu->functions.end(); ++it ) {
481         collect_variables->clear();
482         group_cache.clear();
483         max_var_counter = 0;
484         epsilon_tmp_variable = nullptr;
485         rewrite_float_comp.reset();
486         absint_is_fresh->clear();
487         absint_sfe->clear();
488         absint_giv->clear();
489         absint_max->clear();
490         has_float_comp.reset();
491         AST::FunctionDefinition *fundef = *it;
492 
493         if( !fundef->type->is_extern ) {
494             fundef->accept( collect_variables );
495             fundef->accept( absint_is_fresh );
496             fundef->accept( &has_float_comp );
497             if( !has_float_comp.result_value )
498                 continue;
499             std::cerr << "analyzing " << fundef->type->id << std::endl;
500 
501             if( !use_aposteriori_check  && !find_max_input( fundef, rewrite_float_comp.max_input ) )
502                 CGAL_error_msg("failed to compute max_input for which no overflow occurs");
503             // find max expressions
504             absint_max->visit( fundef );
505             // for all max expressions: eliminate duplicate binary expressions
506             CSE_max cse( this );
507             fundef->accept( &cse );
508             absint_max->clear();
509             absint_max->visit( fundef );
510             // compute floating point errors
511             absint_sfe->visit( fundef );
512             // compute group indices
513             MSG("compute group indices of " << fundef->type->id )
514             absint_giv->visit( fundef );
515             handle( fundef );
516             group_cache.make_partial_order();
517             fundef->accept( &rewrite_float_comp );
518             fundef->type->return_type = filtered_predicate_return_type;
519         }
520     }
521 }
522 
523 
524 // -----------------
525 
526 
527 /*
528 case
529 ===========
530 groups: 1-3
531 group_indices: 1*2*2*3
532 compute max1
533 compute max2
534 compute max3
535 min/max of max123
536 
537 case
538 ============
539 group 1-4
540 group_indices: 0*0*0*0
541 remaining groups: 1 - 4
542 compute max0 (use variables from group 1 - 4)
543 min/max = max0
544 
545 case
546 ============
547 groups: 1 - 5
548 group_indices: 0*0*1*1*2*4
549 compute max1
550 compute max2
551 compute max4
552 remaining groups: 3,5
553 compute max0 (use only those variables that are in group 3 and 5)
554 compute min/max of max0124
555 
556 ==========> approach:
557 g = group_indices
558 zeroes = number of 0 in g
559 g = remove 0 from g
560 
561 for each i in g
562     if not already computed i
563         max_i = compute max value(i)
564     "eps = eps * max_i"
565 if zeroes > 0
566     g = groups_used_in_predicate - g
567     if g' is empty
568         maxvar = max_1
569     else
570         max_0 = compute max value(all i in g)
571         maxvar = max_0
572     "eps = eps * (maxvar ^ zeroes)"
573 
574 */
575 
576 void
visit(AST::UnaryFunction * uf)577 Add_bound_variables::visit( AST::UnaryFunction* uf ) {
578     Generic_visitor::visit( uf );
579     // SQRT and SIGN need special treatment in Rewrite_visitor
580     if( uf->kind == AST::UnaryFunction::XSIGN ) {
581         Variable *new_var = add_tmp_result( uf );
582         Statement_addition_visitor::add_stmt( new AST::VariableDeclaration( new_var ) );
583         if( dynamic_cast<AST::IdentifierExpression*>(uf->e) == nullptr ) {
584             new_var = add_tmp_result( uf->e );
585             Statement_addition_visitor::add_stmt( new AST::VariableDeclaration( new_var ) );
586         }
587         if( epsilon_tmp_variable == nullptr ) {
588             epsilon_tmp_variable = add_variable( "eps", type_float_bound );
589             Statement_addition_visitor::add_stmt_toplevel( new AST::VariableDeclaration( epsilon_tmp_variable ) );
590         }
591 
592         Abstract_value *absval_giv = absint_giv->get_analysis_result( uf->e );
593         Group_index_value *giv = dynamic_cast< Group_index_value* >( absval_giv );
594         assert( giv != nullptr );
595         assert( giv->group_item->degree() > 0 );
596 
597         Group_algebra::Group_varset groups;
598         giv->group_item->collect_groups( groups );
599         Group_algebra::Group_varset::iterator it;
600         for( it = groups.begin(); it != groups.end(); ++it ) {
601             const Group_cache::Group &group = it->second;
602             Group_cache::iterator gcit = group_cache.groups.find( group );
603             if( gcit == group_cache.end() ) {
604                 // we have to allocate a new max variable:
605                 std::stringstream varname;
606                 varname << "max" << ++max_var_counter;
607                 group_cache.add( group, add_variable( varname.str(), type_float ) );
608                 MSG( "adding max var " << varname.str() )
609             }
610         }
611     }
612 }
613 
614 void
visit(AST::BinaryExpression * bexp)615 Add_bound_variables::visit( AST::BinaryExpression* bexp ) {
616     Generic_visitor::visit( bexp );
617     MSG("")
618     if( is_float_or_int( bexp->e1->getType() ) ||
619         is_float_or_int( bexp->e2->getType() )   )
620     {
621         switch( bexp->kind ) {
622         case AST::BinaryExpression::EQ:
623         case AST::BinaryExpression::GEQ:
624         case AST::BinaryExpression::LEQ:
625         case AST::BinaryExpression::NEQ:
626         case AST::BinaryExpression::LE:
627         case AST::BinaryExpression::GR:
628         case AST::BinaryExpression::AND:
629         case AST::BinaryExpression::OR: {
630             Abstract_value *absval_sfe = absint_sfe->get_analysis_result( bexp->e1 );
631             Static_filter_error *sfe1 =  dynamic_cast< Static_filter_error* >( absval_sfe );
632             assert( sfe1 != nullptr );
633             absval_sfe = absint_sfe->get_analysis_result( bexp->e1 );
634             Static_filter_error *sfe2 =  dynamic_cast< Static_filter_error* >( absval_sfe );
635             assert( sfe2 != nullptr );
636             if( !sfe1->is_fresh() || !sfe2->is_fresh() )
637                 throw RuntimeError( "comparison between derived values not yet supported. use 'sign' or 'compare' instead.", bexp->location );
638             break;
639         }
640         default: ;
641         }
642     }
643 }
644 
645 
646 void
visit(AST::UnaryFunction * uf)647 Rewrite_sign_of_fresh_values::visit( AST::UnaryFunction *uf ) {
648     Transformation_visitor::transform<AST::Expression>( uf->e );
649     MSG("")
650     AST::Expression *result = uf;
651     if( uf->kind == AST::UnaryFunction::XSIGN && is_float(uf->e->getType()) ) {
652         AST::BinaryExpression      *bexp = dynamic_cast<AST::BinaryExpression*    >(uf->e);
653         AST::IdentifierExpression *idexp = dynamic_cast<AST::IdentifierExpression*>(uf->e);
654 
655         if( bexp != nullptr )
656         {
657             Error_bound_value *is_fresh1 = dynamic_cast<Error_bound_value*>( add_bounds->absint_is_fresh->get_analysis_result( bexp->e1 ) );
658             Error_bound_value *is_fresh2 = dynamic_cast<Error_bound_value*>( add_bounds->absint_is_fresh->get_analysis_result( bexp->e2 ) );
659             assert( is_fresh1 != nullptr && is_fresh2 != nullptr );
660             if( is_fresh1->is_fresh() && is_fresh2->is_fresh() && bexp->kind == AST::BinaryExpression::SUB ) {
661                 AST::Expression *cond_e =
662                     new AST::ConditionalExpression(
663                         new AST::BinaryExpression( bexp->e1, bexp->e2, AST::BinaryExpression::GR ),
664                         new AST::LiteralExpression( 1 ),
665                         new AST::ConditionalExpression(
666                             new AST::BinaryExpression( bexp->e1, bexp->e2, AST::BinaryExpression::LE ),
667                             new AST::LiteralExpression( -1 ),
668                             new AST::LiteralExpression( 0 )
669                         )
670                     );
671                 result = cond_e;
672             }
673         } else if( idexp != nullptr ) {
674             Error_bound_value *is_fresh = dynamic_cast<Error_bound_value*>( add_bounds->absint_is_fresh->get_analysis_result( idexp ) );
675             assert( is_fresh != nullptr );
676             if( is_fresh->is_fresh() ) {
677                 AST::Expression *cond_e =
678                     new AST::ConditionalExpression(
679                         new AST::BinaryExpression( idexp, new AST::LiteralExpression(0.0), AST::BinaryExpression::GR ),
680                         new AST::LiteralExpression( 1 ),
681                         new AST::ConditionalExpression(
682                             new AST::BinaryExpression( idexp, new AST::LiteralExpression(0.0), AST::BinaryExpression::LE ),
683                             new AST::LiteralExpression( -1 ),
684                             new AST::LiteralExpression( 0 )
685                         )
686                     );
687                 result = cond_e;
688             }
689         }
690     }
691     Transformation_visitor::push( result );
692 }
693 
694 void
compute_max_from(Variable * maxvar,Variable * other,Statement_addition_visitor::Position scope,bool use_fabs)695 Rewrite_float_comparisons::compute_max_from(
696     Variable *maxvar,
697     Variable *other,
698     Statement_addition_visitor::Position scope,
699     bool use_fabs )
700 {
701     assert( maxvar != nullptr );
702     assert( other  != nullptr );
703     //MSG( "maxvar: " << maxvar->id )
704     //MSG( "othervar: " << other->id )
705     AST::Expression *e = new AST::IdentifierExpression(other);
706     if( use_fabs )
707         e = new AST::UnaryFunction( e, AST::UnaryFunction::XABS );
708     /*
709     std::map< Variable*, std::set< Variable* > >::iterator
710         begin = add_bounds->group_cache.subset_relation.begin(),
711         end   = add_bounds->group_cache.subset_relation.end();
712     */
713     if( max_var_cover.find( maxvar ) == max_var_cover.end() ) {
714         add_stmt_with_scope( make_assign_stmt( maxvar, e ), scope );
715     } else {
716         AST::Expression *cond_e =
717             new AST::BinaryExpression(
718                 new AST::IdentifierExpression(maxvar),
719                 e,
720                 AST::BinaryExpression::LE );
721         AST::Statement *s = new AST::ConditionalStatement(
722                 cond_e,
723                 make_assign_stmt( maxvar, e ) );
724         add_stmt_with_scope( s, scope );
725     }
726 }
727 
728 
729 
730 void
make_max_computation(Variable * maxvar)731 Rewrite_float_comparisons::make_max_computation( Variable *maxvar ) {
732     MSG( "maxvar: " << maxvar->id )
733     if( max_var_cover.find( maxvar ) == max_var_cover.end() ) {
734         MSG( maxvar->id << " not yet computed!" )
735         std::set<Variable*>::iterator m_begin = add_bounds->group_cache.subset_relation[maxvar].begin();
736         std::set<Variable*>::iterator m_end   = add_bounds->group_cache.subset_relation[maxvar].end();
737         std::set<Variable*>::iterator v_begin = add_bounds->group_cache.reverse_mapping[ maxvar ].begin();
738         std::set<Variable*>::iterator v_end   = add_bounds->group_cache.reverse_mapping[ maxvar ].end();
739         std::set< Variable* > vars, vars_tmp;
740         //std::copy( m_begin, m_end, std::inserter( vars_tmp, vars_tmp.begin() ) );
741         std::copy( v_begin, v_end, std::inserter( vars_tmp, vars_tmp.begin() ) );
742         std::set_difference( vars_tmp.begin(), vars_tmp.end(),
743                              function_parameters.begin(), function_parameters.end(),
744                              std::inserter( vars, vars.begin() ) );
745         Statement_addition_visitor::Position scope = get_scope( vars );
746         add_stmt_toplevel( new AST::VariableDeclaration( maxvar ) );
747         for( ; m_begin != m_end; ++m_begin ) {
748             Variable *othervar = *m_begin;
749             MSG("iterate: " << othervar->id )
750             make_max_computation( othervar );
751             compute_max_from( maxvar, othervar, scope, false );
752             std::copy( max_var_cover[othervar].begin(), max_var_cover[othervar].end(),
753                        std::inserter( max_var_cover[maxvar], max_var_cover[maxvar].begin() ) );
754         }
755         MSG( maxvar->id << " first iteration ready")
756         assert( add_bounds->group_cache.reverse_mapping.find( maxvar ) != add_bounds->group_cache.reverse_mapping.end() );
757         for( ; v_begin != v_end; ++v_begin ) {
758             if( max_var_cover.find( maxvar ) == max_var_cover.end() ||
759                 max_var_cover[maxvar].find( *v_begin ) == max_var_cover[maxvar].end() )
760             {
761                 MSG("var left: " << (*v_begin)->id )
762                 compute_max_from( maxvar, *v_begin, scope );
763                 max_var_cover[maxvar].insert( *v_begin );
764             }
765         }
766         MSG("finished")
767     }
768 }
769 
770 void
make_max_groups(Group_index_value * giv,double min_input,AST::Expression * & result_cond_underflow,AST::Expression * & result_cond_overflow)771 Rewrite_float_comparisons::make_max_groups( Group_index_value * giv,
772                                             double              min_input,
773                                             AST::Expression   *&result_cond_underflow,
774                                             //AST::Expression   *&result_cond_zero,
775                                             AST::Expression   *&result_cond_overflow  )
776 {
777     assert( giv != nullptr );
778     assert( giv->group_item != nullptr );
779     Group_algebra::Group_varset groups;
780     giv->group_item->collect_groups( groups );
781     Group_algebra::Group_varset::iterator it;
782     // for each input variable degree, store the set of maxvars:
783     std::map< unsigned int, std::set< Variable* > > maxvars_per_degree;
784     for( it = groups.begin(); it != groups.end(); ++it ) {
785         unsigned int                degree = it->first;
786         const std::set< Variable* > &group = it->second;
787         Group_cache::iterator gcit = add_bounds->group_cache.find( group );
788         assert( gcit != add_bounds->group_cache.end() );
789         Variable *var = gcit->second;
790         MSG( "using max var " << var->id );
791         make_max_computation( var );
792         maxvars_per_degree[ degree ].insert( var );
793     }
794     if( add_bounds->use_aposteriori_check )
795         return;
796     std::map< unsigned int, std::set< Variable* > >::iterator it2;
797     for( it2 = maxvars_per_degree.begin(); it2 != maxvars_per_degree.end(); ++it2 ) {
798         unsigned int          degree  = it2->first;
799         std::set< Variable* > maxvar_set = it2->second;
800         std::vector< Variable* > maxvars;
801         std::copy( maxvar_set.begin(), maxvar_set.end(), std::back_inserter( maxvars ) );
802         Variable *lb_var = nullptr, *ub_var = nullptr;
803         derive_min_max( degree, maxvars, lb_var, ub_var );
804 
805         // compute actual allowable min/max input
806         /* for each degree:
807             o maintain a seperate lower/upper bound variable.
808             o "distribute" the bounds uniformly: for degree 2 input variables v2,
809               it is assumed that bound(v2) = bound(v1)^2 so that we have an over/underflow
810               if for one of the different degree's lb/ub variables, the actual input bound
811               violates the precomputed bound. example:
812 
813               if( lb_1 < 1e-60 || lb_2 < 1e-120 )
814                  UNDERFLOW!
815 
816               where lb_1 is a degree 1 lower bound and lb_2 a degree 2 lower bound.
817               of course, other mechanisms are imaginable, but this one is easy and works.
818         */
819         CGAL::FPU_CW_t fpu_backup = CGAL::FPU_get_and_set_cw(CGAL_FE_UPWARD);
820         double actual_min_input = std::pow( min_input, (int)degree );
821         CGAL::FPU_set_cw(CGAL_FE_DOWNWARD);
822         double actual_max_input = std::pow( max_input, (int)degree );
823         CGAL::FPU_set_cw(fpu_backup);
824 
825         AST::Expression *cond_underflow =
826             new AST::BinaryExpression(
827                 new AST::IdentifierExpression( lb_var ),
828                 new AST::LiteralExpression( actual_min_input ),
829                 AST::BinaryExpression::LE
830             );
831         if( result_cond_underflow == nullptr )
832             result_cond_underflow = cond_underflow;
833         else
834             result_cond_underflow =
835                 new AST::BinaryExpression(
836                     result_cond_underflow,
837                     cond_underflow,
838                     AST::BinaryExpression::OR
839                 );
840 
841         /*AST::Expression *cond_zero =
842             new AST::BinaryExpression(
843                 new AST::IdentifierExpression( lb_var ),
844                 new AST::LiteralExpression( 0.0 ),
845                 AST::BinaryExpression::EQ
846             );
847         if( result_cond_zero == nullptr )
848             result_cond_zero = cond_zero;
849         else
850             result_cond_zero =
851                 new AST::BinaryExpression(
852                     result_cond_zero,
853                     cond_zero,
854                     AST::BinaryExpression::AND
855                 );*/
856 
857         AST::Expression *cond_overflow =
858             new AST::BinaryExpression(
859                 new AST::IdentifierExpression( ub_var ),
860                 new AST::LiteralExpression( actual_max_input ),
861                 AST::BinaryExpression::GR
862             );
863         if( result_cond_overflow == nullptr )
864             result_cond_overflow = cond_overflow;
865         else
866             result_cond_overflow =
867                 new AST::BinaryExpression(
868                     result_cond_overflow,
869                     cond_overflow,
870                     AST::BinaryExpression::OR
871                 );
872     }
873 }
874 
875 void
derive_min_max(unsigned int degree,std::vector<Variable * > & max_variables,Variable * & lb_var,Variable * & ub_var)876 Rewrite_float_comparisons::derive_min_max( unsigned int degree,
877                                            std::vector< Variable* > &max_variables,
878                                            Variable *& lb_var,
879                                            Variable *& ub_var )
880 {
881     std::vector< Variable* >::iterator var_it,
882                                        begin = max_variables.begin(),
883                                        end   = max_variables.end();
884     std::set< Variable*> max_ignore;
885     if( max_variables.size() == 1 ) {
886         ub_var = lb_var = max_variables.front();
887         return;
888     } else
889         add_bounds->group_cache.find_maximum( max_variables, max_ignore );
890 
891     if( lb_vars.find( degree ) == lb_vars.end() ) {
892         std::stringstream lb_varname, ub_varname;
893         lb_varname << "lower_bound_" << degree;
894         ub_varname << "upper_bound_" << degree;
895         lb_var = lb_vars[degree] = add_bounds->add_variable( lb_varname.str(), type_float_bound );
896         ub_var = ub_vars[degree] = add_bounds->add_variable( ub_varname.str(), type_float_bound );
897         add_stmt_toplevel( new AST::VariableDeclaration( lb_var ) );
898         add_stmt_toplevel( new AST::VariableDeclaration( ub_var ) );
899     } else {
900         lb_var = lb_vars[degree];
901         ub_var = ub_vars[degree];
902     }
903 
904 
905     bool first = true;
906     for( ; begin != end; ++begin ) {
907         Variable *max_var = *begin;
908         AST::Expression *id_max_var = new AST::IdentifierExpression( max_var );
909         AST::Expression *id_lb_var = new AST::IdentifierExpression( lb_var );
910         AST::Expression *id_ub_var = new AST::IdentifierExpression( ub_var );
911         if( first ) {
912             add_stmt( make_assign_stmt( lb_var, id_max_var ) );
913             add_stmt( make_assign_stmt( ub_var, id_max_var ) );
914             first = false;
915         } else {
916             AST::Expression *cond_max_gt_ub =
917                 new AST::BinaryExpression(
918                     id_max_var,
919                     id_ub_var,
920                     AST::BinaryExpression::GR
921                 );
922             AST::Expression *cond_max_lt_lb =
923                 new AST::BinaryExpression(
924                     id_max_var,
925                     id_lb_var,
926                     AST::BinaryExpression::LE
927                 );
928 
929             bool ub_ignore = max_ignore.find( max_var ) != max_ignore.end();
930             add_stmt(
931                 new AST::ConditionalStatement(
932                     cond_max_lt_lb,
933                     make_assign_stmt( lb_var, id_max_var ),
934                     ub_ignore ? nullptr
935                               : new AST::ConditionalStatement(
936                                   cond_max_gt_ub,
937                                   make_assign_stmt( ub_var, id_max_var ) )
938                 )
939             );
940         }
941     }
942 }
943 
944 AST::Expression*
make_max_term(Group_algebra::Group_item * item)945 Rewrite_float_comparisons::make_max_term( Group_algebra::Group_item *item ) {
946     Group_algebra::Leaf_item *leaf = dynamic_cast<Group_algebra::Leaf_item*>(item);
947     if( leaf != nullptr ) {
948         Group_cache::iterator it = add_bounds->group_cache.find( leaf->variables );
949         assert( it != add_bounds->group_cache.end() );
950         return new AST::IdentifierExpression( it->second );
951     }
952     Group_algebra::Array_item *array = dynamic_cast<Group_algebra::Array_item*>(item);
953     bool is_sum  = dynamic_cast<Group_algebra:: Sum_item*>(item) != nullptr;
954     argused(is_sum);
955     bool is_prod = dynamic_cast<Group_algebra::Product_item*>(item) != nullptr;
956     assert( is_sum || is_prod  );
957     assert( array->items.size() >= 2 );
958     std::vector< Group_algebra::Group_item* >::iterator it = array->items.begin();
959     AST::Expression *result = make_max_term( *it++ );
960     do {
961         AST::Expression *e2 = make_max_term( *it++ );
962         if( is_prod ) result = new AST::BinaryExpression( result, e2, AST::BinaryExpression::MUL );
963         else          result = AST::make_funcall( fun_std_max, result, e2 );
964     } while( it != array->items.end() );
965     return result;
966 }
967 
968 // -----------------------------------
969 
970 void
visit(AST::FunctionDefinition * fundef)971 Rewrite_float_comparisons::visit( AST::FunctionDefinition * fundef ) {
972     function_parameters.clear();
973     std::copy( fundef->type->parameters.begin(), fundef->type->parameters.end(),
974                std::inserter( function_parameters, function_parameters.begin() ) );
975     Transformation_visitor::visit( fundef );
976 }
977 
978 void
visit(AST::UnaryFunction * uf)979 Rewrite_float_comparisons::visit( AST::UnaryFunction *uf ) {
980     Transformation_visitor::transform<AST::Expression>( uf->e );
981 
982     if( uf->kind == AST::UnaryFunction::XSIGN && is_float(uf->e->getType()) ) {
983         Abstract_value *absval_sfe = add_bounds->absint_sfe->get_analysis_result( uf->e );
984         Static_filter_error *sfe =  dynamic_cast< Static_filter_error* >( absval_sfe );
985         assert( sfe != nullptr );
986 
987         Abstract_value *absval_giv = add_bounds->absint_giv->get_analysis_result( uf->e );
988         Group_index_value *giv = dynamic_cast< Group_index_value* >( absval_giv );
989         assert( giv != nullptr );
990         assert( giv->group_item != nullptr );
991 
992         Variable *tmp_result_var = add_bounds->tmp_result_variable( uf );
993         Variable *tmp_arg_var = nullptr;
994         if( add_bounds->has_tmp_result_variable( uf->e ) ) {
995             tmp_arg_var = add_bounds->tmp_result_variable( uf->e );
996             add_stmt( make_assign_stmt( tmp_arg_var, uf->e ) );
997         } else {
998             AST::IdentifierExpression *idexp = dynamic_cast<AST::IdentifierExpression*>(uf->e);
999             assert( idexp != nullptr );
1000             tmp_arg_var = idexp->var;
1001         }
1002         assert( tmp_arg_var != nullptr );
1003         assert( tmp_result_var != nullptr );
1004         //assert( add_bounds->max_result_variables.size() > 0 );
1005         // correction due to the eps computation. for each multiplication, we need to add 'error*ulp'
1006         unsigned int degree = giv->group_item->degree();
1007         assert( degree > 0 );
1008         CGAL::FPU_CW_t fpu_backup = CGAL::FPU_get_and_set_cw(CGAL_FE_UPWARD);
1009         double epsilon = sfe->error().error() +
1010                          sfe->error().error() * CGAL::Static_filter_error::ulp() * (degree-1);
1011         double min_input = std::pow( std::numeric_limits<double>::min() / epsilon,
1012                                      1.0 / degree );
1013         double check_underflow = min_input;
1014         for( unsigned int i = 1; i < degree; ++i )
1015             check_underflow *= min_input;
1016         CGAL::FPU_set_cw(fpu_backup);
1017         assert( check_underflow > std::numeric_limits<double>::min() );
1018         //std::cout << "epsilon: " << epsilon << std::endl;
1019         AST::Expression *eps = new AST::LiteralExpression( epsilon );
1020 
1021         eps = new AST::BinaryExpression( eps, make_max_term( giv->group_item ), AST::BinaryExpression::MUL );
1022         //giv->dump(std::cout);
1023         //uf->e->dump(0);
1024         AST::Expression *cond_underflow = nullptr, /* *cond_zero = nullptr, */ *cond_overflow = nullptr;
1025         make_max_groups( giv, min_input, cond_underflow, /*cond_zero, */cond_overflow );
1026 
1027         static AST::Expression *plus_one  = new AST::LiteralExpression(  +1);
1028         static AST::Expression *minus_one = new AST::LiteralExpression(  -1);
1029 
1030         AST::Expression *cond_det_gt_eps =
1031             new AST::BinaryExpression(
1032                 new AST::IdentifierExpression( tmp_arg_var ),
1033                 new AST::IdentifierExpression( add_bounds->epsilon_tmp_variable ),
1034                 AST::BinaryExpression::GR
1035             );
1036         AST::Expression *cond_det_lt_minus_eps =
1037             new AST::BinaryExpression(
1038                 new AST::IdentifierExpression( tmp_arg_var ),
1039                 new AST::UnaryExpression(
1040                     new AST::IdentifierExpression( add_bounds->epsilon_tmp_variable ),
1041                     AST::UnaryExpression::NEG
1042                 ),
1043                 AST::BinaryExpression::LE
1044             );
1045 
1046         // *** prepare for later use:
1047         AST::StatementList *compare_with_eps = new AST::StatementList;
1048         // check for possible overflow in computation of uf->e, and just return if unsure
1049         if( !add_bounds->use_aposteriori_check ) {
1050             compare_with_eps->add(
1051                 new AST::ConditionalStatement(
1052                     cond_overflow,
1053                     new AST::Return( AST::uncertain_return_value )
1054                 )
1055             );
1056         }
1057 
1058         // compute eps
1059         compare_with_eps->add( make_assign_stmt( add_bounds->epsilon_tmp_variable, eps ) );
1060 
1061         // compare +/- eps with uf->e, returning if unsure
1062         compare_with_eps->add(
1063             new AST::ConditionalStatement(
1064                 cond_det_gt_eps,
1065                 make_assign_stmt( tmp_result_var, plus_one ),
1066                 new AST::ConditionalStatement(
1067                     cond_det_lt_minus_eps,
1068                     make_assign_stmt( tmp_result_var, minus_one ),
1069                     new AST::Return( AST::uncertain_return_value )
1070                 )
1071             )
1072         );
1073 
1074 
1075         if( add_bounds->use_aposteriori_check ) {
1076             compare_with_eps->add(
1077                 new AST::ConditionalStatement(
1078                     make_fe_condition(),
1079                     make_fe_clear_and_fail()
1080                 )
1081             );
1082             add_stmt( compare_with_eps );
1083         } else {
1084             // check for underflow/exact zero:
1085             add_stmt(
1086                 new AST::ConditionalStatement(
1087                     cond_underflow,
1088                     //new AST::ConditionalStatement(
1089                     //    cond_zero,
1090                     //    make_assign_stmt( tmp_result_var, new AST::LiteralExpression(0) ),
1091                         new AST::Return( AST::uncertain_return_value )
1092                     //)
1093                     ,
1094                     // else perform steps described above (see ***)
1095                     compare_with_eps
1096                 )
1097             );
1098         }
1099 
1100         Transformation_visitor::push( new AST::IdentifierExpression( tmp_result_var ) );
1101     } else
1102         Transformation_visitor::push( uf );
1103 }
1104 
1105 void
reset()1106 Rewrite_float_comparisons::reset() {
1107     max_var_cover.clear();
1108     lb_vars.clear();
1109     ub_vars.clear();
1110 }
1111 
1112 
1113