CDT++
Causal Dynamical Triangulations in C++
Loading...
Searching...
No Matches
Utilities_test.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Causal Dynamical Triangulations in C++ using CGAL
3
4 Copyright © 2017 Adam Getchell
5 ******************************************************************************/
6
11
12#include <doctest/doctest.h>
13#include <fmt/ranges.h>
14
15#include <filesystem>
16#include <Manifold.hpp>
17
18using namespace std;
19using namespace utilities;
20
21SCENARIO("Various string/stream/time utilities" *
22 doctest::test_suite("utilities"))
23{
24 spdlog::debug("Various string/stream/time utilities.\n");
25 GIVEN("A topology_type.")
26 {
27 auto constexpr this_topology = topology_type::SPHERICAL;
28 WHEN("Operator<< is invoked.")
29 {
30 stringstream const buffer;
31 std::streambuf* backup = cout.rdbuf(buffer.rdbuf());
32 cout << this_topology;
33 cout.rdbuf(backup);
34 THEN("The output is correct.")
35 {
36 CHECK_EQ(buffer.str(), "spherical");
37 spdlog::debug("buffer.str() contents: {}.\n", buffer.str());
38 }
39 WHEN("fmt::print is invoked.")
40 {
41 THEN("The output is correct.")
42 {
43 auto result = fmt::format("Topology type is: {}.\n", buffer.str());
44 CHECK_EQ(result, "Topology type is: spherical.\n");
45 spdlog::debug("Topology type is: {}.\n", buffer.str());
46 }
47 }
48 }
49 }
50#ifndef _WIN32
51 GIVEN("A running environment.")
52 {
53 WHEN("The current time is requested.")
54 {
55 THEN("The output is correct.")
56 {
57 // Update test yearly
58 auto const result = current_date_time();
59 auto const year = result.find("2025");
60 CHECK_NE(year, std::string::npos);
61 // Human verification
62 fmt::print("Current date and time is: {}\n", current_date_time());
63 }
64 }
65 WHEN("A filename is generated.")
66 {
67 auto constexpr this_topology = topology_type::SPHERICAL;
68 auto constexpr dimensions = 3;
69 auto constexpr simplices = 6700;
70 auto constexpr timeslices = 16;
71 auto const filename =
72 make_filename(this_topology, dimensions, simplices, timeslices,
73 INITIAL_RADIUS, FOLIATION_SPACING);
74 THEN("The output is correct.")
75 {
76 auto const topology = filename.string().find("S3");
77 CHECK_NE(topology, std::string::npos);
78 auto const time = filename.string().find("16");
79 CHECK_NE(time, std::string::npos);
80 auto const cells = filename.string().find("6700");
81 CHECK_NE(cells, std::string::npos);
82 auto const initial_radius = filename.string().find("1.0");
83 CHECK_NE(initial_radius, std::string::npos);
84 auto const file_suffix = filename.string().find("off");
85 CHECK_NE(file_suffix, std::string::npos);
86 // Human verification
87 fmt::print("Filename is: {}\n", filename.string());
88 }
89 }
90 }
91#endif
92}
93
94SCENARIO("Printing Delaunay triangulations" * doctest::test_suite("utilities"))
95{
96 spdlog::debug("Printing Delaunay triangulations.\n");
97 GIVEN("A Delaunay_t<3> triangulation.")
98 {
99 Delaunay_t<3> triangulation;
100 triangulation.insert(Point_t<3>(0, 0, 0));
101 triangulation.insert(Point_t<3>(1, 0, 0));
102 triangulation.insert(Point_t<3>(0, 1, 0));
103 triangulation.insert(Point_t<3>(0, 0, 1));
104 WHEN("The triangulation is printed.")
105 {
106 THEN("No exception is thrown.")
107 {
108 CHECK_NOTHROW(print_delaunay(triangulation));
109 }
110 }
111 }
112}
113
114SCENARIO("Reading and writing Delaunay triangulations to files" *
115 doctest::test_suite("utilities"))
116{
117 spdlog::debug("Reading and writing Delaunay triangulations to files.\n");
118 GIVEN("A Manifold3 constructed from a Delaunay_t<3> triangulation")
119 {
120 Delaunay_t<3> triangulation;
121 triangulation.insert(Point_t<3>(0, 0, 0));
122 triangulation.insert(Point_t<3>(1, 0, 0));
123 triangulation.insert(Point_t<3>(0, 1, 0));
124 triangulation.insert(Point_t<3>(0, 0, 1));
125 // Construct a manifold from a Delaunay triangulation
126 manifolds::Manifold_3 const manifold(
128 auto filename = make_filename(manifold);
129 WHEN("Writing to a file")
130 {
131 write_file(manifold);
132 THEN("The file should exist")
133 {
134 CHECK(std::filesystem::exists(filename));
135 }
136 }
137 WHEN("Reading from a file")
138 {
139 auto triangulation_from_file =
140 utilities::read_file<Delaunay_t<3>>(filename);
141 THEN("The file should contain the triangulation")
142 {
143 REQUIRE(triangulation_from_file.is_valid(true));
144 REQUIRE_EQ(triangulation_from_file.dimension(),
145 manifold.dimensionality());
146 REQUIRE_EQ(triangulation_from_file.number_of_finite_cells(),
147 manifold.N3());
148 REQUIRE_EQ(triangulation_from_file.number_of_finite_facets(),
149 manifold.N2());
150 REQUIRE_EQ(triangulation_from_file.number_of_finite_edges(),
151 manifold.N1());
152 REQUIRE_EQ(triangulation_from_file.number_of_vertices(), manifold.N0());
153 CHECK_EQ(triangulation_from_file, triangulation);
154 }
155 }
156 WHEN("Deleting a file")
157 {
158 std::filesystem::remove(filename);
159 THEN("The file should not exist")
160 {
161 CHECK_FALSE(std::filesystem::exists(filename));
162 }
163 }
164 }
165}
166
167SCENARIO("Randomizing functions" * doctest::test_suite("utilities"))
168{
169 spdlog::debug("Randomizing functions.\n");
170 GIVEN("A PCG die roller")
171 {
172 WHEN("We roll a die twice.")
173 {
174 auto const roll1 = die_roll();
175 auto const roll2 = die_roll();
176 THEN("They should probably be different.") { WARN_NE(roll1, roll2); }
177 }
178 }
179 GIVEN("A container of ints")
180 {
181 Int_precision constexpr VECTOR_TEST_SIZE = 100;
182 array<Int_precision, VECTOR_TEST_SIZE> container{};
183 iota(container.begin(), container.end(), 0);
184 WHEN("The container is shuffled.")
185 {
186 ranges::shuffle(container, make_random_generator());
187 THEN("We get back the elements in random order.")
188 {
189 auto j = 0; // NOLINT
190 for (auto i : container) { WARN_NE(i, j++); } // NOLINT
191 fmt::print("\nShuffled container verification:\n");
192 fmt::print("{}\n", fmt::join(container, " "));
193 }
194 }
195 }
196 GIVEN("A test range of integers")
197 {
198 WHEN("We generate six different random integers within the range.")
199 {
200 auto constexpr min = 64;
201 auto constexpr max = 6400;
202 auto const value1 = generate_random_int(min, max);
203 auto const value2 = generate_random_int(min, max);
204 auto const value3 = generate_random_int(min, max);
205 auto const value4 = generate_random_int(min, max);
206 auto const value5 = generate_random_int(min, max);
207 auto const value6 = generate_random_int(min, max);
208 array container = {value1, value2, value3, value4, value5, value6};
209 THEN("They should all fall within the range and all be different.")
210 {
211 // All elements are >= min
212 CHECK_GE(*ranges::min_element(container), min);
213
214 // All elements are <= max
215 CHECK_LE(*ranges::max_element(container), max);
216
217 // All elements are different
218 ranges::sort(container);
219 CHECK(ranges::is_sorted(container));
220 auto adjacent_iterator = ranges::adjacent_find(container);
221
222 // If the iterator is equal to the end, then all elements are different
223 CHECK_EQ(adjacent_iterator, container.end());
224 }
225 }
226 }
227 GIVEN("A test range of timeslices")
228 {
229 WHEN("We generate six different timeslices within the range.")
230 {
231 auto constexpr max = 256;
232 auto const value1 = generate_random_timeslice(max);
233 auto const value2 = generate_random_timeslice(max);
234 auto const value3 = generate_random_timeslice(max);
235 auto const value4 = generate_random_timeslice(max);
236 auto const value5 = generate_random_timeslice(max);
237 auto const value6 = generate_random_timeslice(max);
238 array container = {value1, value2, value3, value4, value5, value6};
239 THEN("They should all fall within the range and be different.")
240 {
241 auto constexpr min = 1;
242 // All elements are >= min
243 CHECK_GE(*ranges::min_element(container), min);
244
245 // All elements are <= max
246 CHECK_LE(*ranges::max_element(container), max);
247
248 // All elements are different
249 ranges::sort(container);
250 CHECK(ranges::is_sorted(container));
251 auto adjacent_iterator = ranges::adjacent_find(container);
252
253 // If the iterator is equal to the end, then all elements are different
254 CHECK_EQ(adjacent_iterator, container.end());
255 }
256 }
257 }
258 GIVEN("The range between 0 and 1, inclusive")
259 {
260 WHEN("We generate a random real number.")
261 {
262 auto constexpr min = 0.0L;
263 auto constexpr max = 1.0L;
264 auto const value = generate_random_real(min, max);
265 THEN("The real number should lie within that range.")
266 {
267 REQUIRE_LE(min, value);
268 REQUIRE_LE(value, max);
269 }
270 }
271 }
272 GIVEN("A probability generator")
273 {
274 WHEN("We generate six probabilities.")
275 {
276 auto const value1 = generate_probability();
277 auto const value2 = generate_probability();
278 auto const value3 = generate_probability();
279 auto const value4 = generate_probability();
280 auto const value5 = generate_probability();
281 auto const value6 = generate_probability();
282 array container = {value1, value2, value3, value4, value5, value6};
283
284 THEN("They should all be different.")
285 {
286 ranges::sort(container);
287 CHECK(ranges::is_sorted(container));
288 auto adjacent_iterator = ranges::adjacent_find(container);
289
290 // If the iterator is equal to the end, then all elements are different
291 CHECK_EQ(adjacent_iterator, container.end());
292 }
293 }
294 }
295}
296
297SCENARIO("Expected points per timeslice" * doctest::test_suite("utilities"))
298{
299 spdlog::debug("Expected points per timeslice.\n");
300 GIVEN("Simplices and timeslices for various foliations")
301 {
302 WHEN("We request 2 simplices on 2 timeslices.")
303 {
304 THEN("The results are correct.")
305 {
306 REQUIRE_EQ(expected_points_per_timeslice(3, 2, 2), 2);
307 }
308 }
309 WHEN("We request 500 simplices on 4 timeslices.")
310 {
311 THEN("The results are correct.")
312 {
313 REQUIRE_EQ(expected_points_per_timeslice(3, 500, 4), 50);
314 }
315 }
316 WHEN("We request 5000 simplices on 8 timeslices.")
317 {
318 THEN("The results are correct.")
319 {
320 REQUIRE_EQ(expected_points_per_timeslice(3, 5000, 8), 125);
321 }
322 }
323 WHEN("We request 64,000 simplices on 16 timeslices.")
324 {
325 THEN("The results are correct.")
326 {
327 REQUIRE_EQ(expected_points_per_timeslice(3, 64000, 16), 600);
328 }
329 }
330 WHEN("We request 640,000 simplices on 64 timeslices.")
331 {
332 THEN("The results are correct.")
333 {
334 REQUIRE_EQ(expected_points_per_timeslice(3, 640000, 64), 1000);
335 }
336 }
337 WHEN("We specify 4 dimensions")
338 {
339 THEN("A std::invalid_argument exception is thrown.")
340 {
341 REQUIRE_THROWS_AS(expected_points_per_timeslice(4, 640000, 64),
342 std::invalid_argument);
343 }
344 }
345 }
346}
347
348SCENARIO("Exact number (Gmpzf) conversion" * doctest::test_suite("utilities"))
349{
350 spdlog::debug("Exact number (Gmpzf) conversion.\n");
351 GIVEN("A number not exactly representable in binary.")
352 {
353 Gmpzf const TEST_VALUE = 0.17;
354 WHEN("We convert it to double.")
355 {
356 auto const converted_value = Gmpzf_to_double(TEST_VALUE);
357 THEN("It should be exact when converted back from double to Gmpzf.")
358 {
359 REQUIRE_EQ(TEST_VALUE, Gmpzf(converted_value));
360 }
361 }
362 }
363}
Data structures for manifolds.
std::int_fast32_t Int_precision
Definition: Settings.hpp:31
static double constexpr INITIAL_RADIUS
Default foliated triangulation spacings.
Definition: Settings.hpp:47
auto generate_random_timeslice(IntegerType &&t_max_timeslice) noexcept -> decltype(auto)
Generate a random timeslice.
Definition: Utilities.hpp:274
auto die_roll() noexcept
Roll a die with PCG.
Definition: Utilities.hpp:216
void print_delaunay(TriangulationType const &t_triangulation)
Print triangulation statistics.
Definition: Utilities.hpp:144
auto make_filename(topology_type const &t_topology, Int_precision t_dimension, Int_precision t_number_of_simplices, Int_precision t_number_of_timeslices, double t_initial_radius, double t_foliation_spacing) noexcept -> std::filesystem::path
Generate useful filenames.
Definition: Utilities.hpp:93
auto constexpr generate_probability() noexcept
Generate a probability.
Definition: Utilities.hpp:293
auto Gmpzf_to_double(Gmpzf const &t_value) -> double
Convert Gmpzf into a double.
Definition: Utilities.hpp:365
auto current_date_time()
Return current date and time.
Definition: Utilities.hpp:73
auto make_random_generator() noexcept
Make a high-quality random number generator usable by std::shuffle.
Definition: Utilities.hpp:255
void write_file(std::filesystem::path const &filename, TriangulationType triangulation)
Write triangulation to file.
Definition: Utilities.hpp:163
auto constexpr generate_random_int(IntegerType t_min_value, IntegerType t_max_value) noexcept
Generate random integers by calling generate_random, preserves template argument deduction.
Definition: Utilities.hpp:265
auto constexpr generate_random_real(FloatingPointType t_min_value, FloatingPointType t_max_value) noexcept
Generate random real numbers by calling generate_random, preserves template argument deduction.
Definition: Utilities.hpp:284
auto expected_points_per_timeslice(Int_precision const t_dimension, Int_precision t_number_of_simplices, Int_precision t_number_of_timeslices)
Calculate expected # of points per simplex.
Definition: Utilities.hpp:313
SCENARIO("Perform bistellar flip on Delaunay triangulation" *doctest::test_suite("bistellar"))