diff options
Diffstat (limited to 'libs/rational')
-rw-r--r-- | libs/rational/index.html | 3 | ||||
-rw-r--r-- | libs/rational/meta/libraries.json | 14 | ||||
-rw-r--r-- | libs/rational/rational.html | 139 | ||||
-rw-r--r-- | libs/rational/test/rational_example.cpp | 4 | ||||
-rw-r--r-- | libs/rational/test/rational_test.cpp | 167 |
5 files changed, 272 insertions, 55 deletions
diff --git a/libs/rational/index.html b/libs/rational/index.html index bec1cba03..3f3d676e8 100644 --- a/libs/rational/index.html +++ b/libs/rational/index.html @@ -37,10 +37,11 @@ to use one of the more specialist packages available.</p> <p>Revised December 14, 1999</p> -<p>© Copyright Paul Moore 1999. Permission to copy, use, modify, sell +<p>© Copyright Paul Moore 1999. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.</p> +<!-- boostinspect:nolicense (can't find Paul Moore to change license) --> </body> </html> diff --git a/libs/rational/meta/libraries.json b/libs/rational/meta/libraries.json new file mode 100644 index 000000000..471b24297 --- /dev/null +++ b/libs/rational/meta/libraries.json @@ -0,0 +1,14 @@ +{ + "key": "rational", + "name": "Rational", + "authors": [ + "Paul Moore" + ], + "description": "A rational number class.", + "category": [ + "Math" + ], + "maintainers": [ + "Jonathan Turkanis <turkanis -at- coderage.com>" + ] +} diff --git a/libs/rational/rational.html b/libs/rational/rational.html index 32653c876..1198f65a2 100644 --- a/libs/rational/rational.html +++ b/libs/rational/rational.html @@ -55,9 +55,11 @@ public: typedef I int_type; // Constructors - rational(); // Zero - rational(I n); // Equal to n/1 - rational(I n, I d); // General case (n/d) + constexpr rational(); // Zero + constexpr rational(I n); // Equal to n/1 + rational(I n, I d); // General case (n/d) + template<typename J> + constexpr explicit rational(const rational<J> &r); // Cross-instantiation // Normal copy constructors and assignment operators @@ -68,8 +70,8 @@ public: rational& assign(I n, I d); // Representation - I numerator() const; - I denominator() const; + constexpr I numerator() const; + constexpr I denominator() const; // In addition to the following operators, all of the "obvious" derived // operators are available - see <a href="../utility/operators.htm">operators.hpp</a> @@ -91,24 +93,24 @@ public: const rational& operator--(); // Operator not - bool operator!() const; + constexpr bool operator!() const; // Boolean conversion - operator bool_type() const; + constexpr operator bool_type() const; // Comparison operators - bool operator< (const rational& r) const; - bool operator== (const rational& r) const; + bool operator< (const rational& r) const; + constexpr bool operator== (const rational& r) const; // Comparison with integers - bool operator< (I i) const; - bool operator> (I i) const; - bool operator== (I i) const; + bool operator< (I i) const; + bool operator> (I i) const; + constexpr bool operator== (I i) const; }; // Unary operators -template <typename I> rational<I> operator+ (const rational<I>& r); -template <typename I> rational<I> operator- (const rational<I>& r); +template <typename I> constexpr rational<I> operator+ (const rational<I>& r); +template <typename I> rational<I> operator- (const rational<I>& r); // Reversed order operators for - and / between (types convertible to) I and rational template <typename I, typename II> inline rational<I> operator- (II i, const rational<I>& r); @@ -122,7 +124,7 @@ template <typename I> std::istream& operator>> (std::istream& template <typename I> std::ostream& operator<< (std::ostream& os, const rational<I>& r); // Type conversion -template <typename T, typename I> T rational_cast (const rational<I>& r); +template <typename T, typename I> constexpr T rational_cast (const rational<I>& r); </pre> <h2><a name="Rationale">Rationale</a></h2> @@ -167,8 +169,8 @@ these bounds, overflow occurs and the results are undefined. <p> The rational number class is a template to allow the programmer to control the overflow behaviour somewhat. If an unlimited precision integer type is -available, rational numbers based on it will never overflow and will provide -exact calculations in all circumstances. +available, rational numbers based on it will never overflow (modulo resource +limits) and will provide exact calculations in all circumstances. <h2><a name="Integer Type Requirements">Integer Type Requirements</a></h2> @@ -267,7 +269,7 @@ href="#Integer%20Type%20Requirements">any type that can be used</a> with the </table> <p>These function templates now forward calls to their equivalents in the <a -href="../math/">Boost.Math library</a>. Their presence can be controlled at +href="../integer/">Boost.Integer library</a>. Their presence can be controlled at compile time with the <code>BOOST_CONTROL_RATIONAL_HAS_GCD</code> preprocessor constant. @@ -280,18 +282,37 @@ the rational's integer type, or implicitly convertible to that type. (For the two-argument constructor, any needed conversions are evaluated independently, of course.) The components are stored in normalized form. -<p>This implies that the following statements are valid: +<p>Rationals can also be constructed from another rational. When the source and +destination underlying integer types match, the automatically-defined copy- or +move-constructor is used. Otherwise, a converting constructor template is used. +The constructor does member-wise initialization of the numerator and denominator. +Component-level conversions that are marked <code>explicit</code> are fine. When +the conversion ends up value-preserving, it is already normalized; but a check +for normalization is performed in case value-preservation is violated. + +<p>These imply that the following statements are valid: <pre> I n, d; rational<I> zero; rational<I> r1(n); rational<I> r2(n, d); + rational<J> r3(r2); // assuming J(n) and J(d) are well-formed </pre> +<p>The no-argument constructor, single-argument constructor, and cross-version +constructor template are marked as <code>constexpr</code>, making them viable in +constant-expressions when the initializers (if any) are also constant +expressions (and the necessary operations from the underlying integer type(s) +are <code>constexpr</code>-enabled). + <p>The single-argument constructor is <em>not</em> declared as explicit, so there is an implicit conversion from the underlying integer type to the -rational type. +rational type. The two-argument constructor can be considered an implicit +conversion with C++11's uniform initialization syntax, since it is also not +declared explicit. The cross-version constructor template is declared explicit, +so the direction of conversion between two rational instantiations must be +specified. <h3><a name="Arithmetic operations">Arithmetic operations</a></h3> All of the standard numeric operators are defined for the <b>rational</b> @@ -307,8 +328,13 @@ class. These include: == != < > <= >= + + Unary: + - ! </pre> +<p>So far, only <code>operator ==</code>, unary <code>operator +</code>, and +<code>operator !</code> are <code>constexpr</code>-enabled. + <h3><a name="Input and Output">Input and Output</a></h3> Input and output operators <tt><<</tt> and <tt>>></tt> are provided. The external representation of a rational is @@ -319,12 +345,19 @@ external representation of an integer is defined by the undelying integer type. <h3><a name="In-place assignment">In-place assignment</a></h3> -For any <tt>rational<I> r</tt>, <tt>r.assign(n, m)</tt> provides a -fast equivalent of <tt>r = rational<I>(n, m);</tt>, without the +For any <tt>rational<I> r</tt>, <tt>r.assign(n, m)</tt> provides an +alternate to <tt>r = rational<I>(n, m);</tt>, without a user-specified construction of a temporary. While this is probably unnecessary for rationals based on machine integer types, it could offer a saving for rationals based on unlimited-precision integers, for example. +<p>The function will throw if the given components cannot be formed into a valid +rational number. Otherwise, it could throw only if the component-level move +assignment (in C++11; copy-assignment for earlier C++ versions) can throw. The +strong guarantee is kept if throwing happens in the first part, but there is a +risk of neither the strong nor basic guarantees happening if an exception is +thrown during the component assignments. + <h3><a name="Conversions">Conversions</a></h3> <p>There is a conversion operator to an unspecified Boolean type (most likely a member pointer). This operator converts a rational to <code>false</code> if it @@ -339,11 +372,12 @@ for that nature are kept private, should prevent any inappropriate non-Boolean use like numeric or pointer operations or as a <code>switch</code> condition. <p>There are <em>no other</em> implicit conversions from a rational -type. However, there is an explicit type-conversion function, -<tt>rational_cast<T>(r)</tt>. This can be used as follows: +type. Besides the explicit cross-version constructor template, there is an +explicit type-conversion function, <tt>rational_cast<T>(r)</tt>. This can +be used as follows: <pre> - rational r(22,7); + rational<int> r(22,7); double nearly_pi = boost::rational_cast<double>(r); </pre> @@ -351,11 +385,16 @@ type. However, there is an explicit type-conversion function, source rational's numerator or denominator cannot be safely cast to the appropriate floating point type, or if the division of the numerator and denominator (in the target floating point type) does not evaluate correctly. +Also, since this function has a custom name, it cannot be called in generic code +for trading between two instantiations of the same class template, unlike the +cross-version constructor. <p>In essence, all required conversions should be value-preserving, and all operations should behave "sensibly". If these constraints cannot be met, a separate user-defined conversion will be more appropriate. +<p>Boolean conversion and <tt>rational_cast</tt> are <code>constexpr</code>-enabled. + <p><em>Implementation note:</em> <p>The implementation of the rational_cast function was @@ -376,6 +415,7 @@ href="#Integer%20Type%20Requirements">Integer Type Requirements</a>.) <h3><a name="Numerator and Denominator">Numerator and Denominator</a></h3> Finally, access to the internal representation of rationals is provided by the two member functions <tt>numerator()</tt> and <tt>denominator()</tt>. +These functions are <code>constexpr</code>-enabled. <p>These functions allow user code to implement any additional required functionality. In particular, it should be noted that there may be cases where @@ -466,13 +506,17 @@ Specifically, it is likely that performance will be severely sub-optimal. <h2><a name="Exceptions">Exceptions</a></h2> Rationals can never have a denominator of zero. (This library does not support representations for infinity or NaN). Should a rational result ever generate a -denominator of zero, the exception <tt>boost::bad_rational</tt> (a subclass of -<tt>std::domain_error</tt>) is thrown. This should only occur if the user -attempts to explicitly construct a rational with a denominator of zero, or to -divide a rational by a zero value. +denominator of zero, or otherwise fail during normalization, the exception +<tt>boost::bad_rational</tt> (a subclass of <tt>std::domain_error</tt>) is +thrown. This should only occur if the user attempts to explicitly construct a +rational with a denominator of zero, to divide a rational by a zero value, or +generate a negative denominator too large to be normalized. The exception can +be thrown during a cross-instantiation conversion, when at least one of the +components ends up not being value-preserved and the new combination is not +considered normalized. <p>In addition, if operations on the underlying integer type can generate -exceptions, these will be propogated out of the operations on the rational +exceptions, these will be propagated out of the operations on the rational class. No particular assumptions should be made - it is only safe to assume that any exceptions which can be thrown by the integer class could be thrown by any rational operation. In particular, the rational constructor may throw @@ -481,6 +525,11 @@ step. The only exception to this rule is that the rational destructor will only throw exceptions which can be thrown by the destructor of the underlying integer type (usually none). +<p>If the component-level assignment operator(s) can throw, then a rational +object's invariants may be violated if an exception happens during the second +component's assignment. (The <code>assign</code> member function counts here +too.) This violates both the strong and basic guarantees. + <h2><a name="Internal representation">Internal representation</a></h2> <em>Note:</em> This information is for information only. Programs should not be written in such a way as to rely on these implementation details. @@ -513,8 +562,8 @@ The rational number class is designed for use in conjunction with an unlimited precision integer class. With such a class, rationals are always exact, and no problems arise with precision loss, overflow or underflow. -<p>Unfortunately, the C++ standard does not offer such a class (and neither -does boost, at the present time). It is therefore likely that the rational +<p>Unfortunately, the C++ standard does not offer such a class <s>(and neither +does boost, at the present time)</s>. It is therefore likely that the rational number class will in many cases be used with limited-precision integer types, such as the built-in <tt>int</tt> type. @@ -535,6 +584,13 @@ at this end of the representable range, the granularity ia 1. This type of magnitude-dependent granularity is typical of floating point representations. However, it does not "feel" natural when using a rational number class. +<p>Limited-precision integer types may raise issues with the range sizes of +their allowable negative values and positive values. If the negative range is +larger, then the extremely-negative numbers will not have an additive inverse in +the positive range, making them unusable as denominator values since they cannot +be normalized to positive values (unless the user is lucky enough that the input +components are not relatively prime pre-normalization). + <p>It is up to the user of a rational type based on a limited-precision integer type to be aware of, and code in anticipation of, such issues. @@ -632,8 +688,8 @@ required elsewhere, the calculation is performed inline. <h2><a name="References">References</a></h2> <ul> <li>The rational number header itself: <a href="../../boost/rational.hpp">rational.hpp</a> -<li>Some example code: <a href="rational_example.cpp">rational_example.cpp</a> -<li>The regression test: <a href="rational_test.cpp">rational_test.cpp</a> +<li>Some example code: <a href="test/rational_example.cpp">rational_example.cpp</a> +<li>The regression test: <a href="test/rational_test.cpp">rational_test.cpp</a> </ul> <h2><a name="History and Acknowledgements">History and Acknowledgements</a></h2> @@ -667,14 +723,15 @@ on the boost list in January 2001. <p>Daryle Walker provided a Boolean conversion operator, so that a rational can be used in the same Boolean contexts as the built-in numeric types, in December -2005. +2005. He added the cross-instantiation constructor template in August 2013. -<p>Revised November 5, 2006</p> +<p>Revised August 30, 2013</p> -<p>© Copyright Paul Moore 1999-2001; © Daryle Walker 2005. Permission to -copy, use, modify, sell and distribute this document is granted provided this -copyright notice appears in all copies. This document is provided "as -is" without express or implied warranty, and with no claim as to its -suitability for any purpose.</p> +<p>© Copyright Paul Moore 1999-2001; © Daryle Walker 2005, 2013. +Permission to copy, use, modify, sell and distribute this document is granted +provided this copyright notice appears in all copies. This document is provided +"as is" without express or implied warranty, and with no claim as to +its suitability for any purpose.</p> +<!-- boostinspect:nolicense (can't find Paul Moore to change license) --> </body> </html> diff --git a/libs/rational/test/rational_example.cpp b/libs/rational/test/rational_example.cpp index 1438d30bf..16c3fe63c 100644 --- a/libs/rational/test/rational_example.cpp +++ b/libs/rational/test/rational_example.cpp @@ -5,6 +5,10 @@ // appears in all copies. This software is provided "as is" without express or // implied warranty, and with no claim as to its suitability for any purpose. +// boostinspect:nolicense (don't complain about the lack of a Boost license) +// (Paul Moore hasn't been in contact for years, so there's no way to change the +// license.) + // Revision History // 14 Dec 99 Initial version diff --git a/libs/rational/test/rational_test.cpp b/libs/rational/test/rational_test.cpp index 797bfc255..333bf22cc 100644 --- a/libs/rational/test/rational_test.cpp +++ b/libs/rational/test/rational_test.cpp @@ -13,7 +13,15 @@ * extended, by Paul Moore, with permission. */ +// boostinspect:nolicense (don't complain about the lack of a Boost license) +// (Stephen Silver hasn't been contacted yet for permission to change the +// license. If Paul Moore's permission is also needed, then that's a problem +// since he hasn't been in contact for years.) + // Revision History +// 30 Aug 13 Add bug-test of assignments holding the basic and/or strong +// guarantees (Daryle Walker) +// 27 Aug 13 Add test for cross-version constructor template (Daryle Walker) // 23 Aug 13 Add bug-test of narrowing conversions during order comparison; // spell logical-negation in it as "!" because MSVC won't accept // "not" (Daryle Walker) @@ -30,21 +38,21 @@ #define BOOST_TEST_MAIN "Boost::Rational unit tests" #include <boost/config.hpp> +#include <boost/limits.hpp> #include <boost/mpl/list.hpp> #include <boost/operators.hpp> #include <boost/preprocessor/stringize.hpp> -#include <boost/math/common_factor_rt.hpp> +#include <boost/integer/common_factor_rt.hpp> #include <boost/rational.hpp> #include <boost/test/unit_test.hpp> -#include <boost/test/floating_point_comparison.hpp> -#include <boost/test/test_case_template.hpp> #include <climits> +#include <iomanip> +#include <ios> #include <iostream> #include <istream> -#include <limits> #include <ostream> #include <sstream> #include <stdexcept> @@ -231,8 +239,10 @@ class numeric_limits< MyInt > public: static const bool is_specialized = limits_type::is_specialized; - static MyInt min BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return (limits_type::min)(); } - static MyInt max BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return (limits_type::max)(); } + static MyInt min BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return + limits_type::min BOOST_PREVENT_MACRO_SUBSTITUTION (); } + static MyInt max BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return + limits_type::max BOOST_PREVENT_MACRO_SUBSTITUTION (); } static MyInt lowest() throw() { return min BOOST_PREVENT_MACRO_SUBSTITUTION (); } // C++11 @@ -280,8 +290,10 @@ class numeric_limits< MyOverflowingUnsigned > public: static const bool is_specialized = limits_type::is_specialized; - static MyOverflowingUnsigned min BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return (limits_type::min)(); } - static MyOverflowingUnsigned max BOOST_PREVENT_MACRO_SUBSTITUTION () throw() { return (limits_type::max)(); } + static MyOverflowingUnsigned min BOOST_PREVENT_MACRO_SUBSTITUTION () throw() + { return limits_type::min BOOST_PREVENT_MACRO_SUBSTITUTION (); } + static MyOverflowingUnsigned max BOOST_PREVENT_MACRO_SUBSTITUTION () throw() + { return limits_type::max BOOST_PREVENT_MACRO_SUBSTITUTION (); } static MyOverflowingUnsigned lowest() throw() { return min BOOST_PREVENT_MACRO_SUBSTITUTION (); } // C++11 @@ -422,6 +434,7 @@ typedef ::boost::mpl::list<short, int, long, MyInt> all_signed_test_types; ::boost::rational<long> dummy3; ::boost::rational<MyInt> dummy4; ::boost::rational<MyOverflowingUnsigned> dummy5; +::boost::rational<unsigned> dummy6; // Should there be regular tests with unsigned integer types? @@ -797,13 +810,38 @@ BOOST_AUTO_TEST_SUITE_END() // The non-basic rational operations suite BOOST_AUTO_TEST_SUITE( rational_extras_suite ) +#ifndef BOOST_NO_IOSTREAM // Output test BOOST_AUTO_TEST_CASE_TEMPLATE( rational_output_test, T, all_signed_test_types ) { - std::ostringstream oss; + using namespace std; + typedef boost::rational<T> rational_type; + + // Basic test + ostringstream oss; - oss << boost::rational<T>( 44, 14 ); + oss << rational_type( 44, 14 ); BOOST_CHECK_EQUAL( oss.str(), "22/7" ); + + // Width + oss.clear(); oss.str( "" ); + oss << setw( 5 ) << setfill('*') << rational_type( 1, 2 ) << 'r'; + BOOST_CHECK_EQUAL( oss.str(), "**1/2r" ); // not "****1/2r" + + // Positive-sign + oss.clear(); oss.str( "" ); + oss << showpos << rational_type( 2, 3 ) << noshowpos; + BOOST_CHECK_EQUAL( oss.str(), "+2/3" ); // not "+2/+3" + + // Internal padding + oss.clear(); oss.str( "" ); + oss << setw( 8 ) << internal << rational_type( 36, -15 ) << right << 'r'; + BOOST_CHECK_EQUAL( oss.str(), "-***12/5r" ); // not "-*****12/5r" + + // Showbase prefix + oss.clear(); oss.str( "" ); + oss << showbase << hex << rational_type( 34, 987 ) << noshowbase << dec; + BOOST_CHECK_EQUAL( oss.str(), "0x22/3db" ); // not "0x22/0x3db" } // Input test, failing @@ -815,6 +853,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( rational_input_failing_test, T, iss >> r; BOOST_CHECK( !iss ); + BOOST_CHECK( !iss.bad() ); iss.clear(); iss.str( "42" ); @@ -845,6 +884,30 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( rational_input_failing_test, T, iss.str( "1 /2" ); iss >> r; BOOST_CHECK( !iss ); + + // Illegal value check(s) + typedef std::numeric_limits<T> limits_type; + + iss.clear(); + iss.str( "3/0" ); + iss >> r; + BOOST_CHECK( !iss ); + + if ( limits_type::is_signed && limits_type::is_bounded && + limits_type::min BOOST_PREVENT_MACRO_SUBSTITUTION () + + limits_type::max BOOST_PREVENT_MACRO_SUBSTITUTION () < T(0) ) + { + std::ostringstream oss; + + oss << 1 << '/' << limits_type::min BOOST_PREVENT_MACRO_SUBSTITUTION (); + iss.clear(); + iss.str( oss.str() ); + iss.exceptions( std::ios::failbit ); + BOOST_CHECK( iss.good() ); + BOOST_CHECK_THROW( iss >> r, boost::bad_rational ); + BOOST_CHECK( iss.fail() && !iss.bad() ); + iss.exceptions( std::ios::goodbit ); + } } // Input test, passing @@ -871,6 +934,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( rational_input_passing_test, T, BOOST_CHECK( iss >> r ); BOOST_CHECK_EQUAL( r, rational_type(1, 2) ); } +#endif // BOOST_NO_IOSTREAM // Conversion test BOOST_AUTO_TEST_CASE( rational_cast_test ) @@ -896,6 +960,44 @@ BOOST_AUTO_TEST_CASE( rational_cast_test ) MyOverflowingUnsigned(1u) ); } +#ifndef BOOST_NO_MEMBER_TEMPLATES +// Cross-conversion constructor test +BOOST_AUTO_TEST_CASE( rational_cross_constructor_test ) +{ + // This template will be repeated a lot. + using boost::rational; + + // Create a bunch of explicit conversions. + rational<int> const half_i( 2, 4 ); + rational<unsigned> const half_u( half_i ); + rational<MyInt> const half_mi( half_i ); + rational<MyOverflowingUnsigned> const half_mu1(half_u), half_mu2(half_mi); + + BOOST_CHECK_EQUAL( half_u.numerator(), 1u ); + BOOST_CHECK_EQUAL( half_u.denominator(), 2u ); + BOOST_CHECK_EQUAL( half_mi.numerator(), MyInt(1) ); + BOOST_CHECK_EQUAL( half_mi.denominator(), MyInt(2) ); + BOOST_CHECK_EQUAL( half_mu1.numerator(), MyOverflowingUnsigned(1u) ); + BOOST_CHECK_EQUAL( half_mu1.denominator(), MyOverflowingUnsigned(2u) ); + BOOST_CHECK_EQUAL( half_mu2.numerator(), MyOverflowingUnsigned(1u) ); + BOOST_CHECK_EQUAL( half_mu2.denominator(), MyOverflowingUnsigned(2u) ); + +#if 0 + // This will fail since it needs an implicit conversion. + // (Try it if your compiler supports C++11 lambdas.) + BOOST_CHECK( [](rational<unsigned> x){return !!x;}(half_i) ); +#endif + + // Translation from a built-in unsigned type to a signed one is + // implementation-defined, so hopefully we won't get a trap value. + // (We're counting on static_cast<int>(UINT_MAX) being negative.) + rational<unsigned> const too_small( 1u, UINT_MAX ); + rational<int> receiver; + + BOOST_CHECK_THROW( receiver=rational<int>(too_small), boost::bad_rational ); +} +#endif // BOOST_NO_MEMBER_TEMPLATES + // Dice tests (a non-main test) BOOST_AUTO_TEST_CASE_TEMPLATE( dice_roll_test, T, all_signed_test_types ) { @@ -931,8 +1033,8 @@ BOOST_AUTO_TEST_CASE( bug_798357_test ) unsigned const n2 = d1, d2 = UINT_MAX; boost::rational<MyOverflowingUnsigned> const r1( n1, d1 ), r2( n2, d2 ); - BOOST_REQUIRE_EQUAL( boost::math::gcd(n1, d1), 1u ); - BOOST_REQUIRE_EQUAL( boost::math::gcd(n2, d2), 1u ); + BOOST_REQUIRE_EQUAL( boost::integer::gcd(n1, d1), 1u ); + BOOST_REQUIRE_EQUAL( boost::integer::gcd(n2, d2), 1u ); BOOST_REQUIRE( n1 > UINT_MAX / d2 ); BOOST_REQUIRE( n2 > UINT_MAX / d1 ); BOOST_CHECK( r1 < r2 ); @@ -961,7 +1063,7 @@ BOOST_AUTO_TEST_CASE( patch_1438626_test ) // If a GCD routine takes the absolute value of an argument only before // processing, it won't realize that -INT_MIN -> INT_MIN (i.e. no change // from negation) and will propagate a negative sign to its result. - BOOST_REQUIRE_EQUAL( boost::math::gcd(INT_MIN, 6), 2 ); + BOOST_REQUIRE_EQUAL( boost::integer::gcd(INT_MIN, 6), 2 ); // That is bad if the rational number type does not check for that // possibility during normalization. @@ -1008,4 +1110,43 @@ BOOST_AUTO_TEST_CASE( ticket_5855_test ) BOOST_REQUIRE( !dummy ); } +// "rational::assign" doesn't even have the basic guarantee +BOOST_AUTO_TEST_CASE( ticket_9067_test ) +{ + using boost::rational; + using boost::integer::gcd; + + rational<int> a( 6, -8 ); + + // Normalize to maintain invariants + BOOST_CHECK_EQUAL( a.numerator(), -3 ); + BOOST_CHECK_EQUAL( a.denominator(), 4 ); + BOOST_CHECK( a.denominator() > 0 ); + BOOST_CHECK_EQUAL( gcd(a.numerator(), a.denominator()), 1 ); + + // Do we maintain the basic guarantee after a failed component-assign? + BOOST_CHECK_THROW( a.assign(1, 0), boost::bad_rational ); + BOOST_CHECK_NE( a.denominator(), 0 ); + BOOST_CHECK( a.denominator() > 0 ); + BOOST_CHECK_EQUAL( gcd(a.numerator(), a.denominator()), 1 ); + + // Do we get the strong guarantee? + BOOST_CHECK_EQUAL( a.numerator(), -3 ); + BOOST_CHECK_EQUAL( a.denominator(), 4 ); + +#if INT_MIN + INT_MAX < 0 + // Try an example without a zero-denominator + a = rational<int>( -9, 12 ); + BOOST_CHECK_EQUAL( a.numerator(), -3 ); + BOOST_CHECK_EQUAL( a.denominator(), 4 ); + BOOST_CHECK( a.denominator() > 0 ); + BOOST_CHECK_EQUAL( gcd(a.numerator(), a.denominator()), 1 ); + BOOST_CHECK_THROW( a.assign(-(INT_MIN + 1), INT_MIN), boost::bad_rational ); + BOOST_CHECK( a.denominator() > 0 ); + BOOST_CHECK_EQUAL( gcd(a.numerator(), a.denominator()), 1 ); + BOOST_CHECK_EQUAL( a.numerator(), -3 ); + BOOST_CHECK_EQUAL( a.denominator(), 4 ); +#endif +} + BOOST_AUTO_TEST_SUITE_END() |