// Copyright (c) 2002 // Utrecht University (The Netherlands), // ETH Zurich (Switzerland), // INRIA Sophia-Antipolis (France), // Max-Planck-Institute Saarbruecken (Germany), // and Tel-Aviv University (Israel). All rights reserved. // // This file is part of CGAL (www.cgal.org) // // $URL: https://github.com/CGAL/cgal/blob/v5.1/Number_types/include/CGAL/simplest_rational_in_interval.h $ // $Id: simplest_rational_in_interval.h 0779373 2020-03-26T13:31:46+01:00 Sébastien Loriot // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Andreas Fabri, Susan Hert, Sylvain Pion #ifndef CGAL_SIMPLEST_RATIONAL_IN_INTERVAL_H #define CGAL_SIMPLEST_RATIONAL_IN_INTERVAL_H #include #include #include #include #include namespace CGAL { /* simplest_rational_in_interval(x,y) returns the rational number with the smallest denominator in the interval [x,y]. See Knuth, "Seminumerical algorithms", page 654, answer to exercise 4.53-39. */ template Rational simplest_rational_in_interval(double x, double y) { typedef Fraction_traits FT; typedef typename FT::Is_fraction Is_fraction; typedef typename FT::Numerator_type Numerator_type; typedef typename FT::Denominator_type Denominator_type; typedef typename FT::Decompose Decompose; typedef typename FT::Compose Compose; // Must be a fraction CGAL_USE_TYPE(Is_fraction); CGAL_static_assertion((::boost::is_same::value)); // Numerator_type,Denominator_type must be the same CGAL_USE_TYPE(Denominator_type); CGAL_static_assertion((::boost::is_same::value)); if(x == y){ return to_rational(x); } if(x > y){ std::swap(x,y); } Rational r; // Return value. Numerator_type r_numerator, r_denominator; // Deal with negative arguments. We only have to deal with the case // where both x and y are negative -- when exactly one is negative // the best rational in the interval [x,y] is 0. if (x < 0 && y < 0) { // Both arguments are negative: solve positive case and negate return - simplest_rational_in_interval(CGAL::abs(x),CGAL::abs(y)); } else if (x <= 0 || y <= 0) { // One argument is 0, or arguments are on opposite sides of 0: // simplest rational in interval is 0 exactly. r_numerator = 0; r_denominator = 1; } else { // x > 0 && y > 0 double xc = std::floor(1/x); // First coefficient of cf for x. double xr = std::fmod(1/x,1); // Remaining fractional part of x. double yc = std::floor(1/y); // First coefficient of cf for y. double yr = std::fmod(1/y,1); // Remaining fractional part of y. if (xc < yc) { // Return 1/(xc+1). r_numerator = 1; r_denominator = xc + 1; } else if (yc < xc) { // Return 1/(yc+1). r_numerator = 1; r_denominator = yc + 1; } else { // xc == yc // Recurse to find s, the rational with the lowest denominator // between xr and yr. Rational s(simplest_rational_in_interval(xr,yr)); // Return 1/(xc + s). Numerator_type xc_rt(xc); Numerator_type s_num,s_den; Decompose()(s,s_num,s_den); r_numerator = s_den; r_denominator = s_num + xc_rt * s_den; } } return Compose()(r_numerator, r_denominator); } } //namespace CGAL #endif //CGAL_SIMPLEST_RATIONAL_IN_INTERVAL_H