geometry/test/util/rational.cpp
2025-03-18 13:20:09 +02:00

162 lines
5.7 KiB
C++

// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test
// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
// This file was modified by Oracle on 2025.
// Modifications copyright (c) 2025 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <geometry_test_common.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/make.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/util/numeric_cast.hpp>
#include <boost/geometry/util/rational.hpp>
void test_coordinate_cast(std::string const& s, int expected_nom, int expected_denom)
{
boost::rational<int> const a = bg::detail::coordinate_cast<boost::rational<int> >::apply(s);
BOOST_CHECK_EQUAL(a.numerator(), expected_nom);
BOOST_CHECK_EQUAL(a.denominator(), expected_denom);
}
void test_numeric_cast()
{
boost::rational<int> const r1(3, 4);
BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r1), 0.75, 0.00001);
boost::rational<int> const r2(10, 4);
BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r2), 2.5, 0.00001);
BOOST_CHECK_EQUAL(bg::util::numeric_cast<int>(r2), 2);
}
template <typename T>
void test_bounds()
{
using coordinate_t = boost::rational<T>;
auto const lowest = bg::util::bounds<coordinate_t>::lowest();
auto const highest = bg::util::bounds<coordinate_t>::highest();
BOOST_CHECK_MESSAGE(lowest < highest,
"Lowest should be smaller than highest, lowest: " << lowest << " highest: " << highest);
}
// Tests box-related functionality, which depends on geometry::util::bounds
// specialization for Boost.Rational
template <typename T>
void test_box()
{
using coordinate_t = boost::rational<T>;
using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>;
using box_t = bg::model::box<point_t>;
box_t box;
bg::assign_inverse(box);
point_t south_west, north_east;
bg::detail::assign_point_from_index<0>(box, south_west);
bg::detail::assign_point_from_index<1>(box, north_east);
BOOST_CHECK_MESSAGE(bg::get<0>(south_west) > bg::get<0>(north_east),
"Bounding box should be inversed. Now x-min: " << bg::get<0>(south_west)
<< " x-max: " << bg::get<0>(north_east)
<< " " << bg::wkt(box));
BOOST_CHECK_MESSAGE(bg::get<1>(south_west) > bg::get<1>(north_east),
"Bounding box should be inversed. Now y-min: " << bg::get<1>(south_west)
<< " y-max: " << bg::get<1>(north_east)
<< " " << bg::wkt(box));
// Test specifically for points larger than 0, because without specialization Boost.Rational
// will return (0,1) (== 0) by default and code will compile but give wrong results.
bg::expand(box, bg::make<point_t>(4, 4));
bg::expand(box, bg::make<point_t>(8, 8));
// Test within (without specialization, both points are within the box)
auto const point1 = bg::make<point_t>(6, 6);
auto const point2 = bg::make<point_t>(2, 2);
BOOST_CHECK_MESSAGE(bg::within(point1, box),
"Point " << bg::wkt(point1) << " is not within the box " << bg::wkt(box));
BOOST_CHECK_MESSAGE(! bg::within(point2, box),
"Point " << bg::wkt(point2) << " is within the box " << bg::wkt(box));
// Test area (without specialization, it will be 64)
auto const area = bg::util::numeric_cast<T>(bg::area(box));
T const expected_area = 16;
BOOST_CHECK_EQUAL(expected_area, area);
}
void test_select_most_precise()
{
using rational1_t = boost::rational<std::int32_t>;
using rational2_t = boost::rational<std::int64_t>;
using t1 = bg::select_most_precise<double, rational1_t>::type;
using t2 = bg::select_most_precise<double, rational2_t>::type;
using t12 = bg::select_most_precise<rational1_t, rational2_t>::type;
BOOST_CHECK((std::is_same<t1, rational1_t>::value));
BOOST_CHECK((std::is_same<t2, rational2_t>::value));
BOOST_CHECK((std::is_same<t12, rational2_t>::value));
}
void test_wkt(std::string const& wkt, std::string const expected_wkt)
{
bg::model::point<boost::rational<int>, 2, bg::cs::cartesian> p;
bg::read_wkt(wkt, p);
std::ostringstream out;
out << bg::wkt(p);
BOOST_CHECK_EQUAL(out.str(), expected_wkt);
}
int test_main(int, char* [])
{
test_coordinate_cast("0", 0, 1);
test_coordinate_cast("1", 1, 1);
test_coordinate_cast("-1", -1, 1);
test_coordinate_cast("-0.5", -1, 2);
test_coordinate_cast("-1.5", -3, 2);
test_coordinate_cast("0.5", 1, 2);
test_coordinate_cast("1.5", 3, 2);
test_coordinate_cast("2.12345", 42469, 20000);
test_coordinate_cast("1.", 1, 1);
test_coordinate_cast("3/2", 3, 2);
test_coordinate_cast("-3/2", -3, 2);
test_numeric_cast();
test_bounds<std::int16_t>();
test_bounds<std::int32_t>();
test_bounds<std::int64_t>();
test_box<std::int64_t>();
test_select_most_precise();
test_wkt("POINT(1.5 2.75)", "POINT(3/2 11/4)");
test_wkt("POINT(3/2 11/4)", "POINT(3/2 11/4)");
test_wkt("POINT(-1.5 2.75)", "POINT(-3/2 11/4)");
test_wkt("POINT(-3/2 11/4)", "POINT(-3/2 11/4)");
return 0;
}