GiNaCRA
0.6.4
|
00001 /* 00002 * GiNaCRA - GiNaC Real Algebra package 00003 * Copyright (C) 2010-2012 Ulrich Loup, Joachim Redies, Sebastian Junges 00004 * 00005 * This file is part of GiNaCRA. 00006 * 00007 * GiNaCRA is free software: you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation, either version 3 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * GiNaCRA is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with GiNaCRA. If not, see <http://www.gnu.org/licenses/>. 00019 * 00020 */ 00021 00022 00023 // #define GINACRA_OPENINTERVAL_DEBUG 00024 00036 #include <cln/cln.h> 00037 #include <assert.h> 00038 00039 #include "OpenInterval.h" 00040 #include "operators.h" 00041 #include "utilities.h" 00042 00043 namespace GiNaCRA 00044 { 00045 using std::cout; 00046 using std::endl; 00047 00048 // Call GiNaC macro (registrar.h) for completing the implementation into the basic type. This shall be performed AFTER the implementing the classes (here at the end of RealAlgebraicNumberIR.h). 00049 00050 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(OpenInterval, basic, print_func<print_context>( &OpenInterval::do_print )) 00051 00052 // GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(numeric, basic, 00053 // print_func<print_context>(&numeric::do_print). 00054 // print_func<print_latex>(&numeric::do_print_latex). 00055 // print_func<print_csrc>(&numeric::do_print_csrc). 00056 // print_func<print_csrc_cl_N>(&numeric::do_print_csrc_cl_N). 00057 // print_func<print_tree>(&numeric::do_print_tree). 00058 // print_func<print_python_repr>(&numeric::do_print_python_repr)) 00059 00060 00061 // Con- and destructors // 00063 00064 OpenInterval::OpenInterval(): 00065 mLeft( numeric( 0 )), 00066 mRight( numeric( 0 )) 00067 { 00068 setflag( status_flags::expanded ); 00069 } 00070 00071 OpenInterval::OpenInterval( const numeric& n ): 00072 mLeft( numeric( n - 1 )), 00073 mRight( numeric( n + 1 )) 00074 { 00075 setflag( status_flags::expanded ); 00076 } 00077 00078 OpenInterval::OpenInterval( const numeric& l, const numeric& r ) throw ( std::invalid_argument ): 00079 mLeft( numeric( l )), 00080 mRight( numeric( r )) 00081 { 00082 #ifdef GINACRA_OPENINTERVAL_DEBUG 00083 cout << "OpenInterval::OpenInterval " << *this << endl; 00084 #endif 00085 if( l > r ) 00086 throw std::invalid_argument( "Specified bounds do not define an interval." ); 00087 setflag( status_flags::expanded ); 00088 } 00089 00090 OpenInterval::OpenInterval( const OpenInterval& i ): 00091 mLeft( numeric( i.left() )), 00092 mRight( numeric( i.right() )) 00093 { 00094 setflag( status_flags::expanded ); 00095 } 00096 00097 OpenInterval::~OpenInterval(){} 00098 00100 // Selectors // 00102 00103 void OpenInterval::setLeft( const numeric& l ) throw ( std::invalid_argument ) 00104 { 00105 assert( l <= mRight ); // throw std::invalid_argument( "Specified bounds do not define an interval." ); 00106 mLeft = l; 00107 } 00108 00109 void OpenInterval::setRight( const numeric& r ) throw ( std::invalid_argument ) 00110 { 00111 assert( mLeft <= r ); // throw std::invalid_argument( "Specified bounds do not define an interval." ); 00112 mRight = r; 00113 } 00114 00115 const numeric OpenInterval::left() const 00116 { 00117 return mLeft; 00118 } 00119 00120 const numeric OpenInterval::right() const 00121 { 00122 return mRight; 00123 } 00124 00126 // Methods from basic // 00128 00129 int OpenInterval::compare_same_type( const basic& o ) const 00130 { 00131 const OpenInterval& oi = static_cast<const OpenInterval&>(o); 00132 if( this->isEqual( oi )) 00133 return 0; 00134 if( this->isLess( oi )) 00135 return -1; 00136 // if(this->isGreater(oi)) // last case 00137 return 1; 00138 } 00139 00140 bool OpenInterval::is_equal_same_type( const basic& o ) const 00141 { 00142 const OpenInterval& oi = static_cast<const OpenInterval&>(o); 00143 return this->isEqual( oi ); 00144 } 00145 00146 unsigned OpenInterval::calchash() const 00147 { 00148 // use the hash value of the midpoint 00149 hashvalue = ((mLeft + mRight) / numeric( 2 )).gethash(); 00150 this->setflag( status_flags::hash_calculated ); 00151 return hashvalue; 00152 } 00153 00154 void OpenInterval::do_print( const print_context& c, unsigned level ) const 00155 { 00156 c.s << ']' << mLeft << ", " << mRight << '['; 00157 } 00158 00159 ex OpenInterval::evalf( int level ) const 00160 { 00161 return (mLeft + mRight) / numeric( 2 ); 00162 ; // set the evaluated flag 00163 } 00164 00166 // Operations // 00168 00169 const bool OpenInterval::isZero() const 00170 { 00171 return mLeft == numeric( 0 ) && mRight == numeric( 0 ); 00172 } 00173 00174 const bool OpenInterval::isNormalized() const 00175 { 00176 return mLeft > numeric( 0 ) || mRight < numeric( 0 ) || (mLeft == numeric( 0 ) && mRight == numeric( 0 )); 00177 } 00178 00179 const bool OpenInterval::contains( const numeric& n ) const 00180 { 00181 return (mLeft < n && mRight > n) || (n == 0 && mLeft == numeric( 0 ) && mRight == numeric( 0 )); 00182 } 00183 00184 const bool OpenInterval::contains( const OpenInterval& o ) const 00185 { 00186 return mLeft <= o.mLeft && mRight >= o.mRight; 00187 } 00188 00189 const bool OpenInterval::meets( const numeric& n ) const 00190 { 00191 return mLeft <= n && mRight >= n; 00192 } 00193 00194 const OpenInterval OpenInterval::intersection( const OpenInterval& o ) const 00195 { 00196 if( mRight < o.mLeft || o.mRight < mLeft ) // intersection empty 00197 return OpenInterval(); 00198 if( mLeft <= o.mLeft && mRight >= o.mRight ) // this contains o 00199 return OpenInterval( o ); 00200 if( mLeft >= o.mLeft && mRight <= o.mRight ) // o contains this 00201 return OpenInterval( *this ); 00202 if( mLeft <= o.mLeft && mRight <= o.mRight ) // right bound of o outside intersection 00203 return OpenInterval( o.mLeft, mRight ); 00204 if( mLeft >= o.mLeft && mRight >= o.mRight ) // left bound of o outside intersection 00205 return OpenInterval( mLeft, o.mRight ); 00206 // symmetric: 00207 // right bound of this outside intersection 00208 // left bound of this outside intersection 00209 return OpenInterval(); 00210 } 00211 00212 const numeric OpenInterval::midpoint() const 00213 { 00214 return (mLeft + mRight) / numeric( 2 ); 00215 } 00216 00217 const numeric OpenInterval::sample() const 00218 { 00221 if( (mLeft < numeric( 0 ) && mRight > numeric( 0 )) || (mLeft == numeric( 0 ) && mRight == numeric( 0 ))) // exclude most trivial case where the interval contains zero (which probably does not occur in practice) 00222 return numeric( 0 ); 00223 // store real parts as rational numbers (safe because only rational numbers occur in this situation) 00224 cln::cl_RA l = cln::rational( cln::realpart( mLeft.to_cl_N() )); 00225 cln::cl_RA r = cln::rational( cln::realpart( mRight.to_cl_N() )); 00226 cln::cl_RA d = r - l; // distance d positive since r > l in a nonzero open interval 00227 cln::cl_I denominatorD = cln::denominator( d ); // denom(d) = lcm( denom(l), denom(r)) 00228 if( d > 1 ) 00229 { // there is an integer within the isolating interval ( this is necessary but not sufficient ) 00230 if( cln::signum( r ) == -1 ) // both bounds are negative, so r has the smaller number representation 00231 return cln::denominator( r ) == 1 ? numeric( r - 1 ) : numeric( cln::floor1( r )); 00232 else if( cln::signum( l ) == 1 ) // both bounds are positive, so l has the smaller number representation 00233 return cln::denominator( l ) == 1 ? numeric( l + 1 ) : numeric( cln::ceiling1( l )); 00234 else if( cln::signum( r ) == 0 ) // -1 must be in the interval because of d > 1 00235 return numeric( -1 ); 00236 else if( cln::signum( l ) == 0 ) // 1 must be in the interval because of d > 1 00237 return numeric( 1 ); 00238 else // that l is negative and r is positive does not often occur due to normalization of the intervals (zero not contained) 00239 return numeric( 0 ); 00240 } 00241 else if( denominatorD == 1 ) 00242 { // special case: d already is an integer 00243 return numeric( l + (d != 1 ? 1 : d / 2) ); 00244 } 00245 else 00246 { 00247 /* Perform a linear search on the denominators k from 1 to dDenom+1 so that 00248 * - k is minimal (done by iteration order) 00249 * - there is a rational s/k with lN'/lD'=l < s/k < r=rN'/rD', which is equivalent to: 00250 * there is an integer i with lN = lN'*(lcm(k,dDenom)/lD') < i < rN'*(lcm(k,dDenom)/rD') = rN: 00251 * lcm(k, dDenom) divides i 00252 * Remarks: 00253 * - An integer between l and r could still exist, therefore k=1 is checked as well. 00254 * - There is always a rational with denominator dDenom + 1 between l and r (which does not always hold for dDenom!). 00255 * - The integer divisions here are performed as rationals by cutting their denominators, which should always be 1. (Could increase efficiency.) 00256 */ 00257 cln::cl_I numeratorL = cln::numerator( l ); 00258 cln::cl_I numeratorR = cln::numerator( r ); 00259 cln::cl_I denominatorL = cln::denominator( l ); 00260 cln::cl_I denominatorR = cln::denominator( r ); 00261 if( numeratorL < LONG_MAX && numeratorR < LONG_MAX && denominatorL < LONG_MAX && denominatorR < LONG_MAX ) 00262 return OpenInterval::findSample( cln::cl_I_to_long( numeratorL ), 00263 cln::cl_I_to_long( denominatorL ), 00264 cln::cl_I_to_long( numeratorR ), 00265 cln::cl_I_to_long( denominatorR )); 00266 return OpenInterval::findSample( numeratorL, denominatorL, numeratorR, denominatorR ); 00267 } 00268 } 00269 00270 const OpenInterval OpenInterval::abs() const 00271 { 00272 numeric l = GiNaC::abs( mLeft ); 00273 numeric r = GiNaC::abs( mRight ); 00274 if( mLeft == 0 || mRight == 0 || GiNaC::sgn( mLeft ) == GiNaC::sgn( mRight )) 00275 return OpenInterval( std::min<numeric>( l, r ), std::max<numeric>( l, r )); 00276 return OpenInterval( 0, std::max<numeric>( l, r )); 00277 } 00278 00280 // Arithmetic Operations // 00282 00283 const OpenInterval OpenInterval::add( const OpenInterval& o ) const 00284 { 00285 return OpenInterval( mLeft + o.mLeft, mRight + o.mRight ); 00286 } 00287 00288 const OpenInterval OpenInterval::minus() const 00289 { 00290 return OpenInterval( -mRight, -mLeft ); 00291 } 00292 00293 const OpenInterval OpenInterval::mul( const OpenInterval& o ) const 00294 { 00295 numeric min = mLeft * o.mLeft; 00296 numeric max = min; 00297 numeric next = mLeft * o.mRight; 00298 if( next < min ) 00299 min = next; 00300 else if( max != next ) 00301 max = next; 00302 next = mRight * o.mLeft; 00303 if( next < min ) 00304 min = next; 00305 else if( next > max ) 00306 max = next; 00307 next = mRight * o.mRight; 00308 if( next < min ) 00309 min = next; 00310 else if( next > max ) 00311 max = next; 00312 return OpenInterval( min, max ); 00313 } 00314 00315 const OpenInterval OpenInterval::div( const OpenInterval& o ) const throw ( std::invalid_argument ) 00316 { 00317 if( o.contains( 0 )) 00318 throw (std::invalid_argument( "Division by interval containing zero not allowed." )); 00319 numeric min = o.mLeft == 0 ? mLeft / o.mRight : mLeft / o.mLeft; // only one, o.mLeft or o.mRight, might be 0 00320 numeric max = min; 00321 numeric next = o.mRight == 0 ? mLeft / o.mLeft : mLeft / o.mRight; // only one, o.mLeft or o.mRight, might be 0 00322 if( next < min ) 00323 min = next; 00324 else if( max != next ) 00325 max = next; 00326 next = o.mLeft == 0 ? mRight / o.mRight : mRight / o.mLeft; // only one, o.mLeft or o.mRight, might be 0 00327 if( next < min ) 00328 min = next; 00329 else if( next > max ) 00330 max = next; 00331 next = o.mRight == 0 ? mRight / o.mLeft : mRight / o.mRight; // only one, o.mLeft or o.mRight, might be 0 00332 if( next < min ) 00333 min = next; 00334 else if( next > max ) 00335 max = next; 00336 return OpenInterval( min, max ); 00337 } 00338 00339 const OpenInterval OpenInterval::pow( unsigned e ) const 00340 { 00341 if( e % 2 || mLeft >= 0 ) // e is odd or left positive 00342 return OpenInterval( GiNaC::ex_to<numeric>( GiNaC::pow( mLeft, e )), GiNaC::ex_to<numeric>( GiNaC::pow( mRight, e ))); 00343 else if( mRight < 0 ) 00344 return OpenInterval( GiNaC::ex_to<numeric>( GiNaC::pow( mRight, e )), GiNaC::ex_to<numeric>( GiNaC::pow( mLeft, e ))); 00345 return OpenInterval( 0, GiNaC::ex_to<numeric>( std::max<ex>( GiNaC::pow( mRight, e ), GiNaC::pow( mLeft, e )))); 00346 } 00347 00348 OpenInterval OpenInterval::evaluate( const ex& p, evalintervalmap m ) throw ( std::invalid_argument ) 00349 { 00350 if( m.empty() ) 00351 return OpenInterval(); 00352 if( GiNaC::is_exactly_a<numeric>( p )) 00353 return OpenInterval() + GiNaC::ex_to<numeric>( p ); 00354 00356 GiNaC::symbol s = m.begin()->first; 00357 OpenInterval i = m.begin()->second; 00358 OpenInterval result; 00359 if( m.size() == 1 ) 00360 { 00361 for( int d = GiNaC::degree( p, s ); d >= 0; --d ) 00362 { 00363 if( !GiNaC::is_exactly_a<numeric>( GiNaC::coeff( p, s, d ))) 00364 throw std::invalid_argument( "The given polynomial has more variables than defined in the evaluation map." ); 00365 result = GiNaC::ex_to<numeric>( GiNaC::coeff( p, s, d )) + result * i; 00366 } 00367 } 00368 else 00369 { 00370 m.erase( m.begin() ); 00371 for( int d = GiNaC::degree( p, s ); d >= 0; --d ) 00372 result = OpenInterval::evaluate( GiNaC::coeff( p, s, d ), m ) + result * i; 00373 } 00374 return result; 00375 } 00376 00378 // Relational Operations // 00380 00381 const bool OpenInterval::isEqual( const OpenInterval& o ) const 00382 { 00383 return (mLeft == o.mLeft && mRight == o.mRight); // exact same bounds 00384 } 00385 00386 const bool OpenInterval::isLess( const OpenInterval& o ) const 00387 { 00394 return (mLeft <= o.mLeft); // only compare left bounds 00395 } 00396 00397 const bool OpenInterval::isGreater( const OpenInterval& o ) const 00398 { 00405 return (mRight >= o.mRight); // only compare right bounds 00406 } 00407 00409 // AUXILIARY FUNCTIONS // 00411 00412 inline const numeric OpenInterval::findSample( cln::cl_I numeratorL, cln::cl_I denominatorL, cln::cl_I numeratorR, cln::cl_I denominatorR ) 00413 { 00414 cln::cl_I k = 1; // current denominator 00415 cln::cl_I gcdK = 1; 00416 cln::cl_I denominatorLR = cln::lcm( denominatorL, denominatorR ); 00417 cln::cl_I lcmK = cln::numerator( (k * denominatorLR) / gcdK ); 00418 cln::cl_I lN = numeratorL * (cln::numerator( lcmK / denominatorL )); 00419 cln::cl_I rN = numeratorR * (cln::numerator( lcmK / denominatorR )); 00420 while( true ) // search stops at dDenom + 1, which is a possible refinement 00421 { 00422 // alternating search: i iterates from the beginning, j from the end 00423 cln::cl_I i = lN + 1; // strict bounds! 00424 cln::cl_I j = rN - 1; // strict bounds! 00425 while( i <= j ) 00426 { 00427 cln::cl_RA candidate = i / lcmK; // does lcmK divide i ? 00428 if( cln::denominator( candidate ) == k ) // yes! 00429 return numeric( candidate ); 00430 if( j != i ) 00431 { 00432 cln::cl_RA candidate = j / lcmK; // does lcmK divide j ? 00433 if( cln::denominator( candidate ) == k ) // yes! 00434 return numeric( candidate ); 00435 --j; 00436 } 00437 ++i; 00438 } 00439 ++k; 00440 gcdK = cln::gcd( k, denominatorLR ); 00441 lcmK = cln::numerator( (k * denominatorLR) / gcdK ); 00442 lN = numeratorL * (cln::numerator( lcmK / denominatorL )); 00443 rN = numeratorR * (cln::numerator( lcmK / denominatorR )); 00444 } 00445 } 00446 00447 inline const numeric OpenInterval::findSample( long numeratorL, long denominatorL, long numeratorR, long denominatorR ) 00448 { 00449 long k = 1; // current denominator 00450 long gcdK = 1; // = GiNaC::gcd( k, denominatorLR ); 00451 long denominatorLR = GiNaC::lcm( denominatorL, denominatorR ); 00452 long lcmK = GiNaC::numerator( k * denominatorLR, gcdK ); 00453 long lN = numeratorL * (GiNaC::numerator( lcmK, denominatorL )); 00454 long rN = numeratorR * (GiNaC::numerator( lcmK, denominatorR )); 00455 while( true ) // search stops at dDenom + 1, which is a possible refinement 00456 { 00457 // alternating search: i iterates from the beginning, j from the end 00458 long i = lN + 1; // strict bounds! 00459 long j = rN - 1; // strict bounds! 00460 while( i <= j ) 00461 { 00462 if( GiNaC::denominator( i, lcmK ) == k ) // lcmK divides i ! 00463 return numeric( i, lcmK ); 00464 if( j != i ) 00465 { 00466 if( GiNaC::denominator( j, lcmK ) == k ) // lcmK divides j ! 00467 return numeric( j, lcmK ); 00468 --j; 00469 } 00470 ++i; 00471 } 00472 ++k; 00473 gcdK = cln::gcd( k, denominatorLR ); 00474 lcmK = GiNaC::numerator( (k * denominatorLR), gcdK ); 00475 lN = numeratorL * (GiNaC::numerator( lcmK, denominatorL )); 00476 rN = numeratorR * (GiNaC::numerator( lcmK, denominatorR )); 00477 } 00478 } 00479 00480 } // namespace GiNaC 00481