12#include <doctest/doctest.h>
13#include <fmt/ranges.h>
19using namespace utilities;
21SCENARIO(
"Various string/stream/time utilities" *
22 doctest::test_suite(
"utilities"))
24 spdlog::debug(
"Various string/stream/time utilities.\n");
25 GIVEN(
"A topology_type.")
27 auto constexpr this_topology = topology_type::SPHERICAL;
28 WHEN(
"Operator<< is invoked.")
30 stringstream
const buffer;
31 std::streambuf* backup = cout.rdbuf(buffer.rdbuf());
32 cout << this_topology;
34 THEN(
"The output is correct.")
36 CHECK_EQ(buffer.str(),
"spherical");
37 spdlog::debug(
"buffer.str() contents: {}.\n", buffer.str());
39 WHEN(
"fmt::print is invoked.")
41 THEN(
"The output is correct.")
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());
51 GIVEN(
"A running environment.")
53 WHEN(
"The current time is requested.")
55 THEN(
"The output is correct.")
59 auto const year = result.find(
"2025");
60 CHECK_NE(year, std::string::npos);
65 WHEN(
"A filename is generated.")
67 auto constexpr this_topology = topology_type::SPHERICAL;
68 auto constexpr dimensions = 3;
69 auto constexpr simplices = 6700;
70 auto constexpr timeslices = 16;
72 make_filename(this_topology, dimensions, simplices, timeslices,
74 THEN(
"The output is correct.")
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);
87 fmt::print(
"Filename is: {}\n", filename.string());
94SCENARIO(
"Printing Delaunay triangulations" * doctest::test_suite(
"utilities"))
96 spdlog::debug(
"Printing Delaunay triangulations.\n");
97 GIVEN(
"A Delaunay_t<3> triangulation.")
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.")
106 THEN(
"No exception is thrown.")
114SCENARIO(
"Reading and writing Delaunay triangulations to files" *
115 doctest::test_suite(
"utilities"))
117 spdlog::debug(
"Reading and writing Delaunay triangulations to files.\n");
118 GIVEN(
"A Manifold3 constructed from a Delaunay_t<3> triangulation")
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));
129 WHEN(
"Writing to a file")
132 THEN(
"The file should exist")
134 CHECK(std::filesystem::exists(filename));
137 WHEN(
"Reading from a file")
139 auto triangulation_from_file =
140 utilities::read_file<Delaunay_t<3>>(filename);
141 THEN(
"The file should contain the triangulation")
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(),
148 REQUIRE_EQ(triangulation_from_file.number_of_finite_facets(),
150 REQUIRE_EQ(triangulation_from_file.number_of_finite_edges(),
152 REQUIRE_EQ(triangulation_from_file.number_of_vertices(), manifold.N0());
153 CHECK_EQ(triangulation_from_file, triangulation);
156 WHEN(
"Deleting a file")
158 std::filesystem::remove(filename);
159 THEN(
"The file should not exist")
161 CHECK_FALSE(std::filesystem::exists(filename));
167SCENARIO(
"Randomizing functions" * doctest::test_suite(
"utilities"))
169 spdlog::debug(
"Randomizing functions.\n");
170 GIVEN(
"A PCG die roller")
172 WHEN(
"We roll a die twice.")
176 THEN(
"They should probably be different.") { WARN_NE(roll1, roll2); }
179 GIVEN(
"A container of ints")
182 array<Int_precision, VECTOR_TEST_SIZE> container{};
183 iota(container.begin(), container.end(), 0);
184 WHEN(
"The container is shuffled.")
187 THEN(
"We get back the elements in random order.")
190 for (
auto i : container) { WARN_NE(i, j++); }
191 fmt::print(
"\nShuffled container verification:\n");
192 fmt::print(
"{}\n", fmt::join(container,
" "));
196 GIVEN(
"A test range of integers")
198 WHEN(
"We generate six different random integers within the range.")
200 auto constexpr min = 64;
201 auto constexpr max = 6400;
208 array container = {value1, value2, value3, value4, value5, value6};
209 THEN(
"They should all fall within the range and all be different.")
212 CHECK_GE(*ranges::min_element(container), min);
215 CHECK_LE(*ranges::max_element(container), max);
218 ranges::sort(container);
219 CHECK(ranges::is_sorted(container));
220 auto adjacent_iterator = ranges::adjacent_find(container);
223 CHECK_EQ(adjacent_iterator, container.end());
227 GIVEN(
"A test range of timeslices")
229 WHEN(
"We generate six different timeslices within the range.")
231 auto constexpr max = 256;
238 array container = {value1, value2, value3, value4, value5, value6};
239 THEN(
"They should all fall within the range and be different.")
241 auto constexpr min = 1;
243 CHECK_GE(*ranges::min_element(container), min);
246 CHECK_LE(*ranges::max_element(container), max);
249 ranges::sort(container);
250 CHECK(ranges::is_sorted(container));
251 auto adjacent_iterator = ranges::adjacent_find(container);
254 CHECK_EQ(adjacent_iterator, container.end());
258 GIVEN(
"The range between 0 and 1, inclusive")
260 WHEN(
"We generate a random real number.")
262 auto constexpr min = 0.0L;
263 auto constexpr max = 1.0L;
265 THEN(
"The real number should lie within that range.")
267 REQUIRE_LE(min, value);
268 REQUIRE_LE(value, max);
272 GIVEN(
"A probability generator")
274 WHEN(
"We generate six probabilities.")
282 array container = {value1, value2, value3, value4, value5, value6};
284 THEN(
"They should all be different.")
286 ranges::sort(container);
287 CHECK(ranges::is_sorted(container));
288 auto adjacent_iterator = ranges::adjacent_find(container);
291 CHECK_EQ(adjacent_iterator, container.end());
297SCENARIO(
"Expected points per timeslice" * doctest::test_suite(
"utilities"))
299 spdlog::debug(
"Expected points per timeslice.\n");
300 GIVEN(
"Simplices and timeslices for various foliations")
302 WHEN(
"We request 2 simplices on 2 timeslices.")
304 THEN(
"The results are correct.")
309 WHEN(
"We request 500 simplices on 4 timeslices.")
311 THEN(
"The results are correct.")
316 WHEN(
"We request 5000 simplices on 8 timeslices.")
318 THEN(
"The results are correct.")
323 WHEN(
"We request 64,000 simplices on 16 timeslices.")
325 THEN(
"The results are correct.")
330 WHEN(
"We request 640,000 simplices on 64 timeslices.")
332 THEN(
"The results are correct.")
337 WHEN(
"We specify 4 dimensions")
339 THEN(
"A std::invalid_argument exception is thrown.")
342 std::invalid_argument);
348SCENARIO(
"Exact number (Gmpzf) conversion" * doctest::test_suite(
"utilities"))
350 spdlog::debug(
"Exact number (Gmpzf) conversion.\n");
351 GIVEN(
"A number not exactly representable in binary.")
353 Gmpzf
const TEST_VALUE = 0.17;
354 WHEN(
"We convert it to double.")
357 THEN(
"It should be exact when converted back from double to Gmpzf.")
359 REQUIRE_EQ(TEST_VALUE, Gmpzf(converted_value));
Data structures for manifolds.
std::int_fast32_t Int_precision
static double constexpr INITIAL_RADIUS
Default foliated triangulation spacings.
auto generate_random_timeslice(IntegerType &&t_max_timeslice) noexcept -> decltype(auto)
Generate a random timeslice.
auto die_roll() noexcept
Roll a die with PCG.
void print_delaunay(TriangulationType const &t_triangulation)
Print triangulation statistics.
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.
auto constexpr generate_probability() noexcept
Generate a probability.
auto Gmpzf_to_double(Gmpzf const &t_value) -> double
Convert Gmpzf into a double.
auto current_date_time()
Return current date and time.
auto make_random_generator() noexcept
Make a high-quality random number generator usable by std::shuffle.
void write_file(std::filesystem::path const &filename, TriangulationType triangulation)
Write triangulation to file.
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.
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.
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.
SCENARIO("Perform bistellar flip on Delaunay triangulation" *doctest::test_suite("bistellar"))
3D Foliated triangulation