1// Boost.Geometry (aka GGL, Generic Geometry Library)
2// Unit Test
3
4// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands.
5// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
6// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
7
8// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
9// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
10
11// Use, modification and distribution is subject to the Boost Software License,
12// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13// http://www.boost.org/LICENSE_1_0.txt)
14
15
16#include <geometry_test_common.hpp>
17
18#include <boost/geometry/algorithms/area.hpp>
19#include <boost/geometry/algorithms/assign.hpp>
20#include <boost/geometry/algorithms/make.hpp>
21#include <boost/geometry/algorithms/expand.hpp>
22#include <boost/geometry/algorithms/within.hpp>
23#include <boost/geometry/geometries/geometries.hpp>
24#include <boost/geometry/io/wkt/wkt.hpp>
25#include <boost/geometry/util/numeric_cast.hpp>
26#include <boost/geometry/util/rational.hpp>
27
28void test_coordinate_cast(std::string const& s, int expected_nom, int expected_denom)
29{
30 boost::rational<int> const a = bg::detail::coordinate_cast<boost::rational<int> >::apply(source: s);
31 BOOST_CHECK_EQUAL(a.numerator(), expected_nom);
32 BOOST_CHECK_EQUAL(a.denominator(), expected_denom);
33}
34
35void test_numeric_cast()
36{
37 boost::rational<int> const r1(3, 4);
38 BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r1), 0.75, 0.00001);
39
40 boost::rational<int> const r2(10, 4);
41 BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r2), 2.5, 0.00001);
42 BOOST_CHECK_EQUAL(bg::util::numeric_cast<int>(r2), 2);
43}
44
45template <typename T>
46void test_bounds()
47{
48 using coordinate_t = boost::rational<T>;
49 using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>;
50
51 auto const lowest = bg::util::bounds<coordinate_t>::lowest();
52 auto const highest = bg::util::bounds<coordinate_t>::highest();
53
54 BOOST_CHECK_MESSAGE(lowest < highest,
55 "Lowest should be smaller than highest, lowest: " << lowest << " highest: " << highest);
56}
57
58// Tests box-related functionality, which depends on geometry::util::bounds
59// specialization for Boost.Rational
60template <typename T>
61void test_box()
62{
63 using coordinate_t = boost::rational<T>;
64 using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>;
65 using box_t = bg::model::box<point_t>;
66
67 box_t box;
68 bg::assign_inverse(box);
69
70 point_t south_west, north_east;
71 bg::detail::assign_point_from_index<0>(box, south_west);
72 bg::detail::assign_point_from_index<1>(box, north_east);
73
74 BOOST_CHECK_MESSAGE(bg::get<0>(south_west) > bg::get<0>(north_east),
75 "Bounding box should be inversed. Now x-min: " << bg::get<0>(south_west)
76 << " x-max: " << bg::get<0>(north_east)
77 << " " << bg::wkt(box));
78
79 BOOST_CHECK_MESSAGE(bg::get<1>(south_west) > bg::get<1>(north_east),
80 "Bounding box should be inversed. Now y-min: " << bg::get<1>(south_west)
81 << " y-max: " << bg::get<1>(north_east)
82 << " " << bg::wkt(box));
83
84 // Test specifically for points larger than 0, because without specialization Boost.Rational
85 // will return (0,1) (== 0) by default and code will compile but give wrong results.
86 bg::expand(box, bg::make<point_t>(4, 4));
87 bg::expand(box, bg::make<point_t>(8, 8));
88
89 // Test within (without specialization, both points are within the box)
90 auto const point1 = bg::make<point_t>(6, 6);
91 auto const point2 = bg::make<point_t>(2, 2);
92 BOOST_CHECK_MESSAGE(bg::within(point1, box),
93 "Point " << bg::wkt(point1) << " is not within the box " << bg::wkt(box));
94 BOOST_CHECK_MESSAGE(! bg::within(point2, box),
95 "Point " << bg::wkt(point2) << " is within the box " << bg::wkt(box));
96
97 // Test area (without specialization, it will be 64)
98 auto const area = bg::util::numeric_cast<T>(bg::area(box));
99 T const expected_area = 16;
100 BOOST_CHECK_EQUAL(expected_area, area);
101}
102
103void test_select_most_precise()
104{
105 using rational1_t = boost::rational<std::int32_t>;
106 using rational2_t = boost::rational<std::int64_t>;
107
108 using t1 = bg::select_most_precise<double, rational1_t>::type;
109 using t2 = bg::select_most_precise<double, rational2_t>::type;
110 using t12 = bg::select_most_precise<rational1_t, rational2_t>::type;
111
112 BOOST_CHECK((std::is_same<t1, rational1_t>::value));
113 BOOST_CHECK((std::is_same<t2, rational2_t>::value));
114 BOOST_CHECK((std::is_same<t12, rational2_t>::value));
115}
116
117void test_wkt(std::string const& wkt, std::string const expected_wkt)
118{
119 bg::model::point<boost::rational<int>, 2, bg::cs::cartesian> p;
120 bg::read_wkt(wkt, geometry&: p);
121 std::ostringstream out;
122 out << bg::wkt(geometry: p);
123
124 BOOST_CHECK_EQUAL(out.str(), expected_wkt);
125}
126
127int test_main(int, char* [])
128{
129 test_coordinate_cast(s: "0", expected_nom: 0, expected_denom: 1);
130 test_coordinate_cast(s: "1", expected_nom: 1, expected_denom: 1);
131 test_coordinate_cast(s: "-1", expected_nom: -1, expected_denom: 1);
132 test_coordinate_cast(s: "-0.5", expected_nom: -1, expected_denom: 2);
133 test_coordinate_cast(s: "-1.5", expected_nom: -3, expected_denom: 2);
134 test_coordinate_cast(s: "0.5", expected_nom: 1, expected_denom: 2);
135 test_coordinate_cast(s: "1.5", expected_nom: 3, expected_denom: 2);
136 test_coordinate_cast(s: "2.12345", expected_nom: 42469, expected_denom: 20000);
137 test_coordinate_cast(s: "1.", expected_nom: 1, expected_denom: 1);
138
139 test_coordinate_cast(s: "3/2", expected_nom: 3, expected_denom: 2);
140 test_coordinate_cast(s: "-3/2", expected_nom: -3, expected_denom: 2);
141
142 test_numeric_cast();
143
144 test_bounds<std::int16_t>();
145 test_bounds<std::int32_t>();
146 test_bounds<std::int64_t>();
147
148 test_box<std::int64_t>();
149
150 test_select_most_precise();
151
152 test_wkt(wkt: "POINT(1.5 2.75)", expected_wkt: "POINT(3/2 11/4)");
153 test_wkt(wkt: "POINT(3/2 11/4)", expected_wkt: "POINT(3/2 11/4)");
154 test_wkt(wkt: "POINT(-1.5 2.75)", expected_wkt: "POINT(-3/2 11/4)");
155 test_wkt(wkt: "POINT(-3/2 11/4)", expected_wkt: "POINT(-3/2 11/4)");
156
157 return 0;
158}
159

source code of boost/libs/geometry/test/util/rational.cpp