Developer documentation

univariate_bounds.hpp
Go to the documentation of this file.
1 /*******************************************************************
2  * This file is part of the source code of the realroot kernel.
3  * Author(s): Elias P. TSIGARIDAS <et\c di.uoa.gr>
4  * J.P. Pavone, GALAAD, INRIA
5  * V. Sharma, GALAAD, INRIA
6  ********************************************************************/
8 #ifndef realroot_UPOL_BOUND_H
9 #define realroot_UPOL_BOUND_H
10 
12 #include <realroot/GMP.hpp>
14 #include <realroot/univariate.hpp>
15 #include <vector>
16 #include <stack>
17 #include <queue>
18 
19 
20 namespace mmx {
21 
22 using univariate::lcoeff;
23 
24 #ifndef MMX_ABS_FCT
25 #define MMX_ABS_FCT
26  template<typename T> inline T abs(const T& x) { return (x < 0 ? -x : x); }
27 #endif
28 
29 
30  template<class T>
31  T pow2(int i)
32  {
33  assert(i>=0);
34  if(i == 1) return 2;
35  if(i > 0)
36  {
37  T y(2);
38  T z(1);
39  unsigned int n = i;
40  unsigned int m;
41  while (n > 0) {
42  m = n / 2;
43  if (n %2 )
44  {
45  z *= y;
46  }
47  y = y * y;
48  n = m;
49  }
50  return z;
51  }
52  return T(1);
53  }
54 
55 
57  template<class C> struct NISP
58  {
59  //--------------------------------------------------------------------
67  template <typename POLY> static
68  C upper_bound(const POLY& p)
69  {
70  typedef typename texp::rationalof<C>::T rational;
71  // typedef typename NTR::T FT;
72  //typedef double FT;
73  C max(0), sum(0);
74 
75  unsigned i;
76  unsigned n = p.size();
77 
78  // check the degree
79  i = n;
80  while ( p[--i] == 0 );
81  n = i+1;
82  if ( p[i] > 0 )
83  {
84  i = 0;
85  while ( p[i] > 0 && i < n ) i++;
86  if ( i == n ) return 0;
87 
88  for ( unsigned k = i+1; k < n; k ++ )
89  if ( p[k] > 0 ) sum += p[k];
90 
91  for ( unsigned k = i; k < n; k ++ )
92  {
93  if ( p[k] < 0 )
94  { rational tmp = -p[k]/sum; if ( tmp > max ) max = tmp; }
95  else
96  sum -= p[k];
97  };
98  }
99  else
100  {
101  i = 0;
102  while ( p[i] < 0 && i < n ) i++;
103  if ( i == n ) return 0;
104  for ( unsigned k = i+1; k < n; k ++ )
105  if ( p[k] < 0 ) sum += p[k];
106  for ( unsigned k = i; k < n; k ++ )
107  if ( p[k] > 0 ) { rational tmp = -p[k]/sum; if ( tmp > max ) max = tmp; }
108  else sum -= p[k];
109  };
110  max += 1;
111  return max;
112  }
113 
114 
115  template <typename POLY> static
116  C lower_bound(const POLY& p)
117  {
118  typedef typename texp::rationalof<C>::T rational;
119  using univariate::degree;
120 
121  POLY r(p);
122  reverse(r,degree(r));
123  rational m = NISP<rational>::upper_bound(r);
124  return denominator(m)/numerator(m);
125  }
126  };
127 
129  template<class FT> struct NISN
130  {
138  template <typename POLY> static
139  FT upper_bound(const POLY& p)
140  {
141  POLY tmp = p;
142  for ( unsigned i = 1; i < p.size(); i += 2 )
143  tmp[i] = -tmp[i];
144  return -NISP<FT>::upper_bound(tmp);
145  }
146  };
147 
148  //====================================================================
149  template < typename T >
150  struct abs_max : public std::unary_function<T, void>
151  {
152  abs_max() : max(T(-1)) {}
153  void operator() (const T& x)
154  {
155  // using std::abs;
156  T temp = abs(x);
157  if (temp > max) max = temp;
158  }
159  T max;
160  };
161 
163  template<class C> struct Cauchy
164  {
165  //--------------------------------------------------------------------
187  template<class Poly> static
188  C upper_bound(const Poly& p)
189  {
190  //std::cout<< "Cauchy" <<std::endl;
191  typedef typename Poly::value_type RT;
192  //typedef typename texp::rationalof<RT>::T rational;
193 
194  //using std::abs;
195  abs_max<RT> M = std::for_each(p.begin(), p.end()-1, abs_max<RT>());
196  RT an = abs(lcoeff(p));
197  //std::cout<< "Max "<< M.max <<" " <<an<< std::endl;
198  // rational val= rational(M.max)/an + rational(1);
199  C res= C(M.max/an) + C(1);
200  // C res; let::assign(res, val);
201  return res;
202  }
203 
210  template<class Poly>
211  C lower_bound(const Poly& f) const
212  {
213  using univariate::degree;
214 
215  typedef C RT;
216  long deg = degree( f);
217  long l = 0;
218 
219  for (int i = 1; i <= deg; ++i) {
220  if ( sign( f[i]) * sign( f[0]) < 0 ) {
221  ++l;
222  }
223  }
224 
225  long ba0 = bit_size(f[0]) - 1; /* puiss de 2 < an */
226  bool bound_set = false;
227  long K = 0;
228 
229  for (int i = 1; i <= deg; ++i) {
230  if ( sign( f[i]) * sign( f[0]) < 0 ) {
231  long bai = bit_size( RT( l * f[i])) - 1;
232  long p = bai - ba0 - 1 ;
233  long k = i;
234  long q = p / k;
235  long r = p % k;
236 
237  long rq = 0;
238  if (r <= k-2) {
239  rq = q + 1;
240  } else {
241  rq = q + 2;
242  }
243 
244  if ( (bound_set == false) || (K < rq) ) {
245  K = rq;
246  bound_set = true;
247  }
248  }
249  }
250  if ( K < 0 ) {return pow2<RT>( abs( K) );}
251 
252  return 0;
253  }
254 
255  // Return only the power of 2.
256  template<class Poly>
257  long lower_power_2(const Poly& f) const
258  {
259  using univariate::degree;
260 
261  typedef typename Poly::value_type RT;
262 
263  long deg = degree( f);
264  long l = 0;
265 
266  for (int i = 1; i <= deg; ++i) {
267  if ( sign( f[i]) * sign( f[0]) < 0 ) {
268  ++l;
269  }
270  }
271 
272  long ba0 = bit_size(f[0]) - 1; /* puiss de 2 < an */
273  bool bound_set = false;
274  long K = 0;
275 
276  for (int i = 1; i <= deg; ++i) {
277  if ( sign( f[i]) * sign( f[0]) < 0 ) {
278  long bai = bit_size( RT( l * f[i])) - 1;
279  long p = bai - ba0 - 1 ;
280  long k = i;
281  long q = p / k;
282  long r = p % k;
283 
284  long rq = 0;
285  if (r <= k-2) {
286  rq = q + 1;
287  } else {
288  rq = q + 2;
289  }
290 
291  if ( (bound_set == false) || (K < rq) ) {
292  K = rq;
293  bound_set = true;
294  }
295  }
296  }
297  if ( K < 0 ) { abs( K);}
298 
299  return -1;
300  }
301 
302  };
303 
304  template<>
305  template<class Poly>
306  double Cauchy<double>::upper_bound(const Poly& p)
307  {
308  typedef typename Poly::value_type coeff_t;
309  numerics::rounding<double> rnd( numerics::rnd_up() );
310  //using std::abs;
311  coeff_t M=0;
312  for(unsigned i=0;i< p.size(); i++) M = std::max(M, coeff_t(abs(p[i])));
313  // abs_max<coeff_t> M = std::for_each(p.begin(), p.end()-1, abs_max<coeff_t>());
314 
315  coeff_t an = coeff_t(abs(univariate::lcoeff(p)));
316  //std::cout <<M<<" "<<an<<" "<<std::endl;
317 
318  double res=as<double>(coeff_t(M/an)) + 1;
319  // std::cout <<res<<std::endl;
320  return res;
321  }
322 
323  //====================================================================
324  template <typename FT, typename POLY>
325  FT bound_root(const POLY& p, const Cauchy<FT>& m)
326  {
327  return Cauchy<FT>::upper_bound(p);
328  }
329 
330 
331  template < typename FT, typename POLY>
332  FT max_bound(const POLY& p, const Cauchy<FT>& m)
333  {
334  return bound_root(p,m);
335  }
336 
337  template < typename FT, typename POLY>
338  FT
339  min_bound(const POLY& p, Cauchy<FT>)
340  {
341  using univariate::degree;
342 
343  POLY f(p);
345  FT tmp = bound_root(f, Cauchy<FT>());
346  // let::convert(denominator(tmp), numerator(tmp), texp::As<FT>());
347  return FT(1)/tmp;
348  }
349 
350 
351 
352 
353  //====================================================================
359  template<class RT> struct HongBound
360  {
361  template < typename Poly > static
362  RT lower_bound( const Poly& f)
363  {
364  using univariate::degree;
365 
366  unsigned deg = degree(f);
367 
368  long lB=0, gB=0;
369  bool localBoundSet = false, globalBoundSet = false;
370  long temp, q;
371  int s = sign(f[0]);
372  //std::cout << "f[0]= " << f[0] << std::endl;
373  for(int i= deg; i > 0; i--){
374  //std::cout << "f["<< i << "]" << " = " << f[i] << std::endl;
375  if(sign(f[i]) * s < 0){
376  for(int k=i-1; k >= 0; k--){
377  if(sign(f[k]) * s > 0){
378  temp = bit_size( RT(f[i]) ) - bit_size( RT(f[k]) ) - 1;
379  q = temp /(i-k);
380  if(!localBoundSet || lB > q + 2){// Choose the minimum among localBounds
381  localBoundSet = true;
382  lB = q+2;
383  }
384  }
385  }
386  localBoundSet = false;
387  if(!globalBoundSet || gB < lB){// Choose the maximum among globalBounds
388  globalBoundSet = true;
389  gB = lB;
390  }
391  }
392  //std::cout << "Cout gb after " << i << " loop = "<< gB << std::endl;
393  }
394  if ( gB+1 < 0 ) return pow2<RT>( abs( gB+1) );
395  return 0;
396  }
397  };
398 
399  //====================================================================
408  template<class RT>
410  {
411 
412  template < typename Poly > static
413  RT lower_bound( const Poly& f)
414  {
415  using univariate::degree;
416 
417  // std::cout <<"Poly is " << f << std::endl;
418  typedef std::pair<RT, int> Coeff_deg;
419  Coeff_deg T1, T2;
420 
421  unsigned deg=degree(f);
422  int s=sign(f[0]);//, len;
423  long B=0;// The logarithm of the bound to be returned
424  bool boundSet=false;// The bound is not set initially
425  long temp,q;
426  int posCounter=0, negCounter=0, nPosCounter=0;
427  // The coefficients from posCounter to negCounter-1 have the same sign
428  // as f[0]; the coefficients from negCounter to nPosCounter-1 have the
429  // opposite sign of f[0].
430  std::queue< Coeff_deg > Neg;// Queue that stores the negative (between
431  // negCounter and nPosCounter) coefficients.
432  std::stack< Coeff_deg > Pos; // Stack that stores the positive (between
433  // posCounter and negCounter) coefficients.
434 
435  int l1, l2;// size of the above two queues
436 
437  while(posCounter != int(deg+1)){
438  // Find the first change in sign larger than posCounter and assign negCounter
439  // to this value. If no such sign variation occurs then
440  // set negCounter to -1.
441  negCounter = -1;// By default there is no such coefficient
442  //Pos.push(Coeff_deg(RT(f[posCounter]), posCounter));// Push the first "positive" coefficient
443  Pos.push(Coeff_deg(RT(f[posCounter]), posCounter));// Push the first "positive" coefficient
444  for(unsigned i=posCounter + 1;i <= deg; i++){
445  if(sign(f[i]) * s < 0){
446  negCounter=i; break;
447  }
448  if(sign(f[i]) *s > 0){// Store the list of "positive" coefficients
449  //Pos.push(Coeff_deg(RT(f[i]), i));// Note the actual degree is deg-i, but
450  // later we subtract degrees so we store only i
451  Pos.push(Coeff_deg(RT(f[i]), i));
452  }
453  }
454  if(negCounter == -1){
455  // std::cout <<"Returning the lower bound " << B << std::endl;
456  if ( B < 0 )
457  return pow2<RT>( abs(B) );
458  else
459  return 0;
460  }
461  // Now find the next coefficient that has the same sign as f[0].
462  nPosCounter = deg+1;// By default this is deg+1
463  Neg.push(Coeff_deg(RT(f[negCounter]), negCounter));
464  for(unsigned i=negCounter + 1;i <= deg; i++){
465  if(sign(f[i]) * s > 0){
466  nPosCounter=i; break;
467  }
468  if(sign(f[i]) *s < 0)// Store the list of "negative" coefficients
469  Neg.push(Coeff_deg(RT(f[i]), i));
470  }
471 
472  // std::cout <<" Assigning to the queues done" << std::endl;
473  // Start computing the bound
474  // If the number of "positive" terms from posCounter to negCounter -1 are
475  // greater or equal to the number of "negative" terms from negCounter to
476  // nPosCounter -1 then we have the straightforward matching of "negative"
477  // coefficients with the "positive" ones.
478  l1 = Neg.size(); l2 = Pos.size();
479  if(l2 >= l1){
480  // std::cout <<"Pos length " << l2 << " greater than Neg length "
481  // << l1 << std::endl;
482  // or equally negCounter - posCounter >= nPosCounter - negCounter
483  for(int i=0; i < l1; i++){
484  T1= Neg.front(); Neg.pop();
485  //T2= Pos.front(); Pos.pop();
486  T2=Pos.top();Pos.pop();
487  // std::cout <<"Neg coeff " << T1.first << " Pos coeff " << T2.first
488  // << " Deg difference " << T1.second - T2.second
489  // << std::endl;
490  temp = bit_size( T1.first ) - bit_size( T2.first ) - 1;
491  q = temp/(T1.second - T2.second);
492  // std::cout << "Difference between logs " << temp
493  // << " temporary bound value " << B << std::endl;
494  if(!boundSet || B < q + 2){// Choose the maximum
495  boundSet = true;
496  B = q+2;
497  }
498  }
499  }else{//Else split the coefficient f[posCounter] to compensate for
500  // the paucity of "positive" terms and then do the matching.
501  // std::cout <<" Pos length "<< l2 << " smaller than Neg length "
502  // << l1 << std::endl;
503 
504  //T2=Pos.front(); Pos.pop();
505  T2= Pos.top();Pos.pop();
506  for(int i=0; i <= l1-l2; i++){
507  T1= Neg.front(); Neg.pop();
508  temp = bit_size( T1.first ) - bit_size( T2.first ) + bit_size(RT(l1-l2+1)) - 1;
509  q = temp/(T1.second-T2.second);
510  if(!boundSet || B < q + 2){// Choose the maximum
511  boundSet = true;
512  B = q+2;
513  }
514  }
515  // std::cout <<" Splitting the leading coefficient done" << std::endl;
516  l1 = Neg.size();// Now the size of Neg and Pos should be the same
517  for(int i=0; i < l1; i++){
518  T1= Neg.front(); Neg.pop();
519  //T2= Pos.front(); Pos.pop();
520  Pos.pop();
521  temp = bit_size( T1.first ) - bit_size( T2.first ) - 1;
522  q = temp/(T1.second - T2.second);
523  if(!boundSet || B < q + 2){// Choose the maximum
524  boundSet = true;
525  B = q+2;
526  }
527  }
528  }// end else
529  // std::cout <<" Matching the coefficients done" << std::endl;
530  posCounter = nPosCounter; Neg.empty(); Pos.empty();
531  }//end while
532  // std::cout <<"Returning the lower bound " << B << std::endl;
533  if ( B < 0 ) return pow2<RT>( abs(B) );
534  return 0;
535  }
536 
537 
538  template < typename Poly > static
539  long lower_power_2( const Poly& f)
540  {
541  using univariate::degree;
542 
543  // std::cout <<"Poly is " << f << std::endl;
544  typedef std::pair<RT, int> Coeff_deg;
545  Coeff_deg T1, T2;
546 
547  unsigned deg=degree(f);
548  int s=sign(f[0]);//, len;
549  long B=0;// The logarithm of the bound to be returned
550  bool boundSet=false;// The bound is not set initially
551  long temp,q;
552  int posCounter=0, negCounter=0, nPosCounter=0;
553  // The coefficients from posCounter to negCounter-1 have the same sign
554  // as f[0]; the coefficients from negCounter to nPosCounter-1 have the
555  // opposite sign of f[0].
556  std::queue< Coeff_deg > Neg;// Queue that stores the negative (between
557  // negCounter and nPosCounter) coefficients.
558  std::stack< Coeff_deg > Pos; // Stack that stores the positive (between
559  // posCounter and negCounter) coefficients.
560 
561  int l1, l2;// size of the above two queues
562 
563  while(posCounter != int(deg+1)){
564  // Find the first change in sign larger than posCounter and assign negCounter
565  // to this value. If no such sign variation occurs then
566  // set negCounter to -1.
567  negCounter = -1;// By default there is no such coefficient
568  //Pos.push(Coeff_deg(RT(f[posCounter]), posCounter));// Push the first "positive" coefficient
569  Pos.push(Coeff_deg(RT(f[posCounter]), posCounter));// Push the first "positive" coefficient
570  for(unsigned i=posCounter + 1;i <= deg; i++){
571  if(sign(f[i]) * s < 0){
572  negCounter=i; break;
573  }
574  if(sign(f[i]) *s > 0){// Store the list of "positive" coefficients
575  //Pos.push(Coeff_deg(RT(f[i]), i));// Note the actual degree is deg-i, but
576  // later we subtract degrees so we store only i
577  Pos.push(Coeff_deg(RT(f[i]), i));
578  }
579  }
580  if(negCounter == -1){
581  // std::cout <<"Returning the lower bound " << B << std::endl;
582  if ( B < 0 ) return abs( B);
583  else return -1;
584  }
585  // Now find the next coefficient that has the same sign as f[0].
586  nPosCounter = deg+1;// By default this is deg+1
587  Neg.push(Coeff_deg(RT(f[negCounter]), negCounter));
588  for(unsigned i=negCounter + 1;i <= deg; i++){
589  if(sign(f[i]) * s > 0){
590  nPosCounter=i; break;
591  }
592  if(sign(f[i]) *s < 0)// Store the list of "negative" coefficients
593  Neg.push(Coeff_deg(RT(f[i]), i));
594  }
595 
596  // std::cout <<" Assigning to the queues done" << std::endl;
597  // Start computing the bound
598  // If the number of "positive" terms from posCounter to negCounter -1 are
599  // greater or equal to the number of "negative" terms from negCounter to
600  // nPosCounter -1 then we have the straightforward matching of "negative"
601  // coefficients with the "positive" ones.
602  l1 = Neg.size(); l2 = Pos.size();
603  if(l2 >= l1){
604  // std::cout <<"Pos length " << l2 << " greater than Neg length "
605  // << l1 << std::endl;
606  // or equally negCounter - posCounter >= nPosCounter - negCounter
607  for(int i=0; i < l1; i++){
608  T1= Neg.front(); Neg.pop();
609  //T2= Pos.front(); Pos.pop();
610  T2=Pos.top();Pos.pop();
611  // std::cout <<"Neg coeff " << T1.first << " Pos coeff " << T2.first
612  // << " Deg difference " << T1.second - T2.second
613  // << std::endl;
614  temp = bit_size( T1.first ) - bit_size( T2.first ) - 1;
615  q = temp/(T1.second - T2.second);
616  // std::cout << "Difference between logs " << temp
617  // << " temporary bound value " << B << std::endl;
618  if(!boundSet || B < q + 2){// Choose the maximum
619  boundSet = true;
620  B = q+2;
621  }
622  }
623  }else{//Else split the coefficient f[posCounter] to compensate for
624  // the paucity of "positive" terms and then do the matching.
625  // std::cout <<" Pos length "<< l2 << " smaller than Neg length "
626  // << l1 << std::endl;
627 
628  //T2=Pos.front(); Pos.pop();
629  T2= Pos.top();Pos.pop();
630  for(int i=0; i <= l1-l2; i++){
631  T1= Neg.front(); Neg.pop();
632  temp = bit_size( T1.first ) - bit_size( T2.first ) + bit_size(RT(l1-l2+1)) - 1;
633  q = temp/(T1.second-T2.second);
634  if(!boundSet || B < q + 2){// Choose the maximum
635  boundSet = true;
636  B = q+2;
637  }
638  }
639  // std::cout <<" Splitting the leading coefficient done" << std::endl;
640  l1 = Neg.size();// Now the size of Neg and Pos should be the same
641  for(int i=0; i < l1; i++){
642  T1= Neg.front(); Neg.pop();
643  //T2= Pos.front(); Pos.pop();
644  Pos.pop();
645  temp = bit_size( T1.first ) - bit_size( T2.first ) - 1;
646  q = temp/(T1.second - T2.second);
647  if(!boundSet || B < q + 2){// Choose the maximum
648  boundSet = true;
649  B = q+2;
650  }
651  }
652  }// end else
653  // std::cout <<" Matching the coefficients done" << std::endl;
654  posCounter = nPosCounter; Neg.empty(); Pos.empty();
655  }//end while
656  // std::cout <<"Returning the lower bound " << B << std::endl;
657  if ( B < 0 ) return abs( B);
658  else return -1;
659 
660  }
661  };
662  //====================================================================
663 
664 } //namespace mmx
665 //--------------------------------------------------------------------
666 #endif // realroot_UPOL_BOUND_H
667 
T pow2(int i)
Definition: univariate_bounds.hpp:31
Cauchy bound.
Definition: univariate_bounds.hpp:163
static C lower_bound(const POLY &p)
Definition: univariate_bounds.hpp:116
Definition: univariate_bounds.hpp:409
dynamic_exp< E >::degree_t degree(const dynamic_exp< E > &t)
Definition: dynamicexp.hpp:191
R::value_type lcoeff(const R &p)
Definition: univariate.hpp:357
int degree(const R &p)
Definition: univariate.hpp:194
FT bound_root(const POLY &p, const Cauchy< FT > &m)
Definition: univariate_bounds.hpp:325
abs_max()
Definition: univariate_bounds.hpp:152
T max
Definition: univariate_bounds.hpp:159
Definition: univariate_bounds.hpp:359
void reverse(Poly &R, const Poly &P)
Definition: contfrac_intervaldata.hpp:139
static C upper_bound(const POLY &p)
Computes the "Negative Inverse Sum" bound for the positive roots.
Definition: univariate_bounds.hpp:68
C lower_bound(const Poly &f) const
Computes the Cauchy lower bound on the first positive root as a power of 2.
Definition: univariate_bounds.hpp:211
void reverse(R &p, typename R::size_type n)
Definition: univariate.hpp:590
kernel_rationalof< typename kernelof< X >::T, _X, typename is_extended< X >::T >::T T
Definition: texp_rationalof.hpp:21
Definition: rounding_mode.hpp:71
static RT lower_bound(const Poly &f)
Definition: univariate_bounds.hpp:413
Definition: univariate_bounds.hpp:150
real_t sum(real_t const *const src, unsigned sz, int st=1)
Definition: loops_vctops.hpp:197
ZZ numerator(const QQ &a)
Definition: GMPXX.hpp:95
long int bit_size(const ZZ &z)
Definition: GMPXX.hpp:32
void operator()(const T &x)
Definition: univariate_bounds.hpp:153
FT min_bound(const POLY &p, Cauchy< FT >)
Definition: univariate_bounds.hpp:339
Negative Inverse Sum bound for negative roots.
Definition: univariate_bounds.hpp:129
void abs(Interval< C, r > &x, const Interval< C, r > &a)
Definition: Interval_fcts.hpp:185
long lower_power_2(const Poly &f) const
Definition: univariate_bounds.hpp:257
static long lower_power_2(const Poly &f)
Definition: univariate_bounds.hpp:539
ZZ denominator(const QQ &a)
Definition: GMPXX.hpp:96
static FT upper_bound(const POLY &p)
Definition: univariate_bounds.hpp:139
FT max_bound(const POLY &p, const Cauchy< FT > &m)
Definition: univariate_bounds.hpp:332
static C upper_bound(const Poly &p)
Computes the Cauchy root bound.
Definition: univariate_bounds.hpp:188
double C
Definition: solver_mv_fatarcs.cpp:16
int sign(const QQ &a)
Definition: GMP.hpp:60
Interval< T, r > max(const Interval< T, r > &a, const Interval< T, r > &b)
Definition: Interval_fcts.hpp:135
Definition: array.hpp:12
Negative Inverse Sum bound for positive roots.
Definition: univariate_bounds.hpp:57
static RT lower_bound(const Poly &f)
Definition: univariate_bounds.hpp:362
#define assert(expr, msg)
Definition: shared_object.hpp:57
Home