GiNaCRA  0.6.4
OpenInterval.cpp
Go to the documentation of this file.
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