diff --git a/docs/layouts/coordinates.rst b/docs/layouts/coordinates.rst index fbf153d3c..036112ea7 100644 --- a/docs/layouts/coordinates.rst +++ b/docs/layouts/coordinates.rst @@ -12,8 +12,6 @@ An offset coordinate is a coordinate that defines a location via an offset from .. doxygenstruct:: fiction::offset::ucoord_t -.. doxygenclass:: fiction::offset::coord_iterator - Cube coordinates ---------------- @@ -23,13 +21,20 @@ At the same time, they can be used to address 3-dimensional grids. .. doxygenstruct:: fiction::cube::coord_t SiQAD coordinates ----------------- +----------------- -SiQAD coordinates are used to describe locations of Silicon Dangling Bonds on the H-Si(100) 2x1 surface were dimer columns and rows are identified by x and y values, respecitvely, +SiQAD coordinates are used to describe locations of Silicon Dangling Bonds on the H-Si(100) 2x1 surface were dimer columns and rows are identified by x and y values, respectively, while the z value (0,1) points to the top or bottom Si atom in the dimer. The coordinates are originally used in the SiQAD simulator (https://github.com/siqad). .. doxygenstruct:: fiction::siqad::coord_t +Coordinate iterator +------------------- + +An iterator type that allows to enumerate coordinates in order within a boundary. + +.. doxygenclass:: fiction::coord_iterator + Utility functions ----------------- diff --git a/include/fiction/io/print_layout.hpp b/include/fiction/io/print_layout.hpp index dc78a2ed6..4e2c24333 100644 --- a/include/fiction/io/print_layout.hpp +++ b/include/fiction/io/print_layout.hpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -368,51 +367,46 @@ void print_charge_layout(std::ostream& os, const charge_distribution_surface min{std::max(min_x - 2, 0), std::max(min_y - 1, 0)}; const coordinate max{std::min(max_x + 2, cds.x()), std::min(max_y + 1, cds.y())}; - // loop over dimer pairs - for (decltype(cds.y()) y_pos = min.y; y_pos <= max.y; ++y_pos) - { - // loop over rows of a dimer pair - for (uint8_t r = 0; r <= 1; ++r) + // iterate over all coordinates in the rows determined by the vertical crop + cds.foreach_coordinate( + [&](const coordinate& c) { - for (decltype(cds.x()) x_pos = min.x; x_pos <= max.x; ++x_pos) + if (c.x < min.x || c.x > max.x) // apply horizontal crop { - const cell c{x_pos, y_pos, r}; + return; + } - if (cds.is_empty_cell(c)) + switch (cds.get_charge_state(c)) // switch over the charge state of the SiDB at the current coordinate + { + case sidb_charge_state::NEGATIVE: { - os << (draw_lattice ? fmt::format(cs_color ? detail::SIDB_LAT_COLOR : detail::NO_COLOR, " · ") : - " "); - continue; + os << fmt::format(cs_color ? detail::SIDB_NEG_COLOR : detail::NO_COLOR, " ● "); + break; } - - // switch over the charge state of the SiDB index associated with the current cell, and update count - switch (cds.get_charge_state(c)) + case sidb_charge_state::POSITIVE: { - case sidb_charge_state::NEGATIVE: - { - os << fmt::format(cs_color ? detail::SIDB_NEG_COLOR : detail::NO_COLOR, " ● "); - continue; - } - case sidb_charge_state::POSITIVE: - { - os << fmt::format(cs_color ? detail::SIDB_POS_COLOR : detail::NO_COLOR, " ⨁ "); - continue; - } - case sidb_charge_state::NEUTRAL: - { - os << fmt::format(cs_color ? detail::SIDB_NEUT_COLOR : detail::NO_COLOR, " ◯ "); - continue; - } - default: // NONE charge state case - { - os << fmt::format(cs_color ? detail::SIDB_LAT_COLOR : detail::NO_COLOR, " ◌ "); - } + os << fmt::format(cs_color ? detail::SIDB_POS_COLOR : detail::NO_COLOR, " ⨁ "); + break; + } + case sidb_charge_state::NEUTRAL: + { + os << fmt::format(cs_color ? detail::SIDB_NEUT_COLOR : detail::NO_COLOR, " ◯ "); + break; + } + default: // NONE charge state case -> empty cell + { + os << (draw_lattice || !cds.is_empty_cell(c) ? + fmt::format(cs_color ? detail::SIDB_LAT_COLOR : detail::NO_COLOR, " · ") : + " "); } } - os << '\n'; - } - os << '\n'; - } + + if (c.x == max.x) + { + os << (c.z == 1 ? "\n\n" : "\n"); + } + }, + min, {0, max.y + 1, 0}); // flush stream os << std::endl; diff --git a/include/fiction/layouts/cartesian_layout.hpp b/include/fiction/layouts/cartesian_layout.hpp index 9004dee86..7e9bd3748 100644 --- a/include/fiction/layouts/cartesian_layout.hpp +++ b/include/fiction/layouts/cartesian_layout.hpp @@ -621,15 +621,15 @@ class cartesian_layout * will be iterated first, then y, then z. * * @param start First coordinate to include in the range of all coordinates. - * @param stop Last coordinate to include in the range of all coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all coordinates. * @return An iterator range from `start` to `stop`. If they are not provided, the first/last coordinate is used as * a default. */ [[nodiscard]] auto coordinates(const OffsetCoordinateType& start = {}, const OffsetCoordinateType& stop = {}) const { - return range_t{std::make_pair( - offset::coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop})}; + return range_t{ + std::make_pair(coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop})}; } /** * Applies a function to all coordinates accessible in the layout between `start` and `stop`. The iteration order is @@ -638,22 +638,22 @@ class cartesian_layout * @tparam Fn Functor type that has to comply with the restrictions imposed by `mockturtle::foreach_element`. * @param fn Functor to apply to each coordinate in the range. * @param start First coordinate to include in the range of all coordinates. - * @param stop Last coordinate to include in the range of all coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all coordinates. */ template void foreach_coordinate(Fn&& fn, const OffsetCoordinateType& start = {}, const OffsetCoordinateType& stop = {}) const { mockturtle::detail::foreach_element( - offset::coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop}, fn); + coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop}, fn); } /** * Returns a range of all coordinates accessible in the layout's ground layer between `start` and `stop`. The * iteration order is the same as for the coordinates function but without the z dimension. * * @param start First coordinate to include in the range of all ground coordinates. - * @param stop Last coordinate to include in the range of all ground coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all ground coordinates. * @return An iterator range from `start` to `stop`. If they are not provided, the first/last coordinate in the * ground layer is used as a default. */ @@ -665,8 +665,8 @@ class cartesian_layout const auto ground_layer = aspect_ratio{x(), y(), 0}; return range_t{ - std::make_pair(offset::coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop})}; + std::make_pair(coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop})}; } /** * Applies a function to all coordinates accessible in the layout's ground layer between `start` and `stop`. The @@ -675,7 +675,7 @@ class cartesian_layout * @tparam Fn Functor type that has to comply with the restrictions imposed by `mockturtle::foreach_element`. * @param fn Functor to apply to each coordinate in the range. * @param start First coordinate to include in the range of all ground coordinates. - * @param stop Last coordinate to include in the range of all ground coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all ground coordinates. */ template void foreach_ground_coordinate(Fn&& fn, const OffsetCoordinateType& start = {}, @@ -686,8 +686,8 @@ class cartesian_layout const auto ground_layer = aspect_ratio{x(), y(), 0}; mockturtle::detail::foreach_element( - offset::coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop}, fn); + coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop}, fn); } /** * Returns a container that contains all coordinates that are adjacent to a given one. Thereby, only cardinal diff --git a/include/fiction/layouts/coordinates.hpp b/include/fiction/layouts/coordinates.hpp index 061724cff..533563a0b 100644 --- a/include/fiction/layouts/coordinates.hpp +++ b/include/fiction/layouts/coordinates.hpp @@ -7,9 +7,11 @@ #include +#include #include #include #include +#include // data types cannot properly be converted to bit field types #pragma GCC diagnostic push @@ -25,6 +27,7 @@ namespace fiction */ namespace offset { + /** * Unsigned offset coordinates. * @@ -252,114 +255,6 @@ inline std::ostream& operator<<(std::ostream& os, const ucoord_t& t) return os; } -/** - * An iterator type that allows to enumerate coordinates in order within a boundary. - * - * @tparam CoordinateType Type of coordinate to enumerate. - */ -template -class coord_iterator -{ - public: - using value_type = CoordinateType; - /** - * Standard constructor. Initializes the iterator with a starting position and the boundary within to enumerate. - * - * With `dimension = (1, 2, 1)` and `start = (0, 0, 0)`, the following order would be enumerated: - * - * - (0, 0, 0) - * - (1, 0, 0) - * - (0, 1, 0) - * - (1, 1, 0) - * - (0, 2, 0) - * - (1, 2, 0) - * - (0, 0, 1) - * - (1, 0, 1) - * - (0, 1, 1) - * - (1, 1, 1) - * - (0, 2, 1) - * - (1, 2, 1) - * - * coord_iterator is compatible with the STL forward_iterator category. - * - * @param dimension Boundary within to enumerate. Iteration wraps at its limits. - * @param start Starting coordinate to enumerate first. - */ - constexpr explicit coord_iterator(const CoordinateType& dimension, const CoordinateType& start) noexcept : - aspect_ratio{dimension}, - coord{start} - {} - /** - * Increments the iterator. - * - * @return Reference to the incremented iterator. - */ - constexpr coord_iterator& operator++() noexcept - { - if (coord != aspect_ratio) - { - ++coord.x; - - if (coord.x > aspect_ratio.x) - { - coord.x = 0; - - ++coord.y; - if (coord.y > aspect_ratio.y) - { - coord.y = 0; - - ++coord.z; - } - } - } - else - { - coord = coord.get_dead(); - } - - return *this; - } - - constexpr coord_iterator operator++(int) noexcept - { - const auto result{*this}; - - ++(*this); - - return result; - } - - constexpr CoordinateType operator*() const noexcept - { - return coord; - } - - constexpr bool operator==(const coord_iterator& other) const noexcept - { - return (coord == other.coord); - } - - constexpr bool operator!=(const coord_iterator& other) const noexcept - { - return !(*this == other); - } - - constexpr bool operator<(const coord_iterator& other) const noexcept - { - return (coord < other.coord); - } - - constexpr bool operator<=(const coord_iterator& other) const noexcept - { - return (coord <= other.coord); - } - - private: - const CoordinateType aspect_ratio; - - CoordinateType coord; -}; } // namespace offset /** @@ -368,6 +263,7 @@ class coord_iterator */ namespace cube { + /** * Signed cube coordinates. * @@ -832,10 +728,156 @@ constexpr coord_t to_siqad_coord(const CoordinateType& coord) noexcept } // namespace siqad +/** + * An iterator type that allows to enumerate coordinates in order within a boundary. + * + * @tparam CoordinateType Type of coordinate to enumerate. + */ +template +class coord_iterator +{ + public: + using value_type = CoordinateType; + /** + * Standard constructor. Initializes the iterator with a starting position and the boundary within to enumerate. + * + * With `dimension = (1, 2, 1)` and `start = (0, 0, 0)`, the following order would be enumerated for offset or cubic + * coordinates: + * + * - (0, 0, 0) + * - (1, 0, 0) + * - (0, 1, 0) + * - (1, 1, 0) + * - (0, 2, 0) + * - (1, 2, 0) + * - (0, 0, 1) + * - (1, 0, 1) + * - (0, 1, 1) + * - (1, 1, 1) + * - (0, 2, 1) + * - (1, 2, 1) + * + * For SiQAD coordinates with the same parameters, we have the following order of enumeration: + * + * - (0, 0, 0) + * - (1, 0, 0) + * - (0, 0, 1) + * - (1, 0, 1) + * - (0, 1, 0) + * - (1, 2, 0) + * - (0, 1, 1) + * - (1, 1, 1) + * - (1, 1, 0) + * - (0, 2, 0) + * - (0, 2, 1) + * - (1, 2, 1) + * + * coord_iterator is compatible with the STL forward_iterator category. + * + * @param dimension Boundary within to enumerate. Iteration wraps at its limits. + * @param start Starting coordinate to enumerate first. + */ + constexpr explicit coord_iterator(const CoordinateType& dimension, const CoordinateType& start) noexcept : + aspect_ratio{dimension}, + coord{start} + { + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v, + "CoordinateType must be a supported coordinate"); + } + /** + * Increments the iterator. + * + * @return Reference to the incremented iterator. + */ + constexpr coord_iterator& operator++() noexcept + { + if (coord != aspect_ratio) + { + ++coord.x; + + if (coord.x > aspect_ratio.x) + { + coord.x = 0; + + if constexpr (std::is_same_v || + std::is_same_v) + { + ++coord.y; + if (coord.y > aspect_ratio.y) + { + coord.y = 0; + + ++coord.z; + } + } + else if constexpr (std::is_same_v) + { + coord.y += coord.z; + coord.z = !coord.z; + } + else + { + assert(false && "Unsupported coordinate type"); + } + } + } + else + { + coord = coord.get_dead(); + } + + return *this; + } + + constexpr coord_iterator operator++(int) noexcept + { + const auto result{*this}; + + ++(*this); + + return result; + } + + constexpr CoordinateType operator*() const noexcept + { + return coord; + } + + constexpr bool operator==(const coord_iterator& other) const noexcept + { + return (coord == other.coord); + } + + constexpr bool operator!=(const coord_iterator& other) const noexcept + { + return !(*this == other); + } + + constexpr bool operator<(const coord_iterator& other) const noexcept + { + return (coord < other.coord); + } + + constexpr bool operator<=(const coord_iterator& other) const noexcept + { + return (coord <= other.coord); + } + + private: + const CoordinateType aspect_ratio; + + CoordinateType coord; +}; + } // namespace fiction +// NOLINTBEGIN(cert-dcl58-cpp) + namespace std { + // define std::hash overload for offset::ucoord_t template <> struct hash @@ -866,17 +908,21 @@ struct hash } }; -// make offset::coord_iterator compatible with STL iterator categories +// make coord_iterator compatible with STL iterator categories template -struct iterator_traits> +struct iterator_traits> { using iterator_category = std::forward_iterator_tag; using value_type = Coordinate; }; + } // namespace std +// NOLINTEND(cert-dcl58-cpp) + namespace fmt { + // make offset::ucoord_t compatible with fmt::format template <> struct formatter @@ -909,6 +955,23 @@ struct formatter return format_to(ctx.out(), "({},{},{})", c.x, c.y, c.z); } }; +// make siqad::coord_t compatible with fmt::format +template <> +struct formatter +{ + template + constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format(const fiction::siqad::coord_t& c, FormatContext& ctx) + { + return format_to(ctx.out(), "({},{},{})", c.x, c.y, c.z); + } +}; + } // namespace fmt #pragma GCC diagnostic pop diff --git a/include/fiction/layouts/hexagonal_layout.hpp b/include/fiction/layouts/hexagonal_layout.hpp index ebdd1685c..0a6e45a42 100644 --- a/include/fiction/layouts/hexagonal_layout.hpp +++ b/include/fiction/layouts/hexagonal_layout.hpp @@ -793,15 +793,15 @@ class hexagonal_layout * will be iterated first, then y, then z. * * @param start First coordinate to include in the range of all coordinates. - * @param stop Last coordinate to include in the range of all coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all coordinates. * @return An iterator range from `start` to `stop`. If they are not provided, the first/last coordinate is used as * a default. */ [[nodiscard]] auto coordinates(const OffsetCoordinateType& start = {}, const OffsetCoordinateType& stop = {}) const { - return range_t{std::make_pair( - offset::coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop})}; + return range_t{ + std::make_pair(coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop})}; } /** * Applies a function to all coordinates accessible in the layout between `start` and `stop`. The iteration order is @@ -810,22 +810,22 @@ class hexagonal_layout * @tparam Fn Functor type that has to comply with the restrictions imposed by `mockturtle::foreach_element`. * @param fn Functor to apply to each coordinate in the range. * @param start First coordinate to include in the range of all coordinates. - * @param stop Last coordinate to include in the range of all coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all coordinates. */ template void foreach_coordinate(Fn&& fn, const OffsetCoordinateType& start = {}, const OffsetCoordinateType& stop = {}) const { mockturtle::detail::foreach_element( - offset::coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop}, fn); + coord_iterator{strg->dimension, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{strg->dimension, stop.is_dead() ? strg->dimension.get_dead() : stop}, fn); } /** * Returns a range of all coordinates accessible in the layout's ground layer between `start` and `stop`. The * iteration order is the same as for the coordinates function but without the z dimension. * * @param start First coordinate to include in the range of all ground coordinates. - * @param stop Last coordinate to include in the range of all ground coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all ground coordinates. * @return An iterator range from `start` to `stop`. If they are not provided, the first/last coordinate in the * ground layer is used as a default. */ @@ -837,8 +837,8 @@ class hexagonal_layout auto ground_layer = aspect_ratio{x(), y(), 0}; return range_t{ - std::make_pair(offset::coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop})}; + std::make_pair(coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop})}; } /** * Applies a function to all coordinates accessible in the layout's ground layer between `start` and `stop`. The @@ -847,7 +847,7 @@ class hexagonal_layout * @tparam Fn Functor type that has to comply with the restrictions imposed by `mockturtle::foreach_element`. * @param fn Functor to apply to each coordinate in the range. * @param start First coordinate to include in the range of all ground coordinates. - * @param stop Last coordinate to include in the range of all ground coordinates. + * @param stop Last coordinate (exclusive) to include in the range of all ground coordinates. */ template void foreach_ground_coordinate(Fn&& fn, const OffsetCoordinateType& start = {}, @@ -858,8 +858,8 @@ class hexagonal_layout auto ground_layer = aspect_ratio{x(), y(), 0}; mockturtle::detail::foreach_element( - offset::coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, - offset::coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop}, fn); + coord_iterator{ground_layer, start.is_dead() ? OffsetCoordinateType{0, 0} : start}, + coord_iterator{ground_layer, stop.is_dead() ? ground_layer.get_dead() : stop}, fn); } /** * Returns a container that contains all coordinates that are adjacent to a given one. Thereby, cardinal and ordinal diff --git a/test/io/print_layout.cpp b/test/io/print_layout.cpp index ba05903e7..777ace9ff 100644 --- a/test/io/print_layout.cpp +++ b/test/io/print_layout.cpp @@ -215,7 +215,7 @@ TEST_CASE("Print Bestagon OR-gate", "[print-charge-layout]") { using hex_gate_lyt = hex_even_row_gate_clk_lyt; - hex_gate_lyt layout{{1, 0}}; + hex_gate_lyt layout{aspect_ratio{1, 0, 1}}; layout.create_or({}, {}, {0, 0}); @@ -253,7 +253,7 @@ TEST_CASE("Print Bestagon OR-gate", "[print-charge-layout]") " · · ◯ · · · · · · · · · · · · · · · · · · · · · · · · · ● · · \n" " · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · \n" "\n" - " · · · · ◌ · · · · · · · · · · · · · · · · · · · · · ● · · · · \n" + " · · · · · · · · · · · · · · · · · · · · · · · · · · ● · · · · \n" " · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · \n" "\n" " · · · · · · · · ● · · · · · · · · · · · · · ◯ · · · · · · · · \n" @@ -272,7 +272,7 @@ TEST_CASE("Print Bestagon OR-gate", "[print-charge-layout]") " · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · \n" "\n" " · · · · · · · · · · · · · · · · · · ⨁ · · · · · · · · · · · · \n" - " · · · · · · · · · · · · · ◌ · · · · · · · · · · · · · · · · · \n" + " · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · \n" "\n" " · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · \n" " · · · · · · · · · · · · · · · ● · · · · · · · · · · · · · · · \n" diff --git a/test/layouts/coordinates.cpp b/test/layouts/coordinates.cpp index 141580175..aaaf40853 100644 --- a/test/layouts/coordinates.cpp +++ b/test/layouts/coordinates.cpp @@ -2,13 +2,17 @@ // Created by marcel on 15.09.21. // +#include #include +#include #include +#include + #include -#include #include +#include using namespace fiction; @@ -65,7 +69,7 @@ TEST_CASE("Unsigned offset coordinates", "[coordinates]") CHECK(t1 < t3); CHECK(t2 < t3); - std::map coordinate_repr{ + const std::map coordinate_repr{ {0x8000000000000000, coordinate{}}, {0x0000000000000000, coordinate{0, 0, 0}}, {0x4000000000000000, coordinate{0, 0, 1}}, {0x4000000080000001, coordinate{1, 1, 1}}, {0x0000000000000002, coordinate{2, 0, 0}}, {0x3fffffffffffffff, coordinate{2147483647, 2147483647, 0}}}; @@ -84,9 +88,8 @@ TEST_CASE("Unsigned offset coordinates", "[coordinates]") CHECK(os.str() == "(3,2,1)"); } -TEST_CASE("siqad coordinate conversion", "[coordinates]") +TEST_CASE("SiQAD coordinate conversion", "[coordinates]") { - using coordinate = siqad::coord_t; using coordinate_fiction = cube::coord_t; @@ -124,6 +127,61 @@ TEST_CASE("siqad coordinate conversion", "[coordinates]") CHECK(t5_fiction.y == -3); } +TEMPLATE_TEST_CASE("Coordinate iteration", "[coordinates]", offset::ucoord_t, cube::coord_t, siqad::coord_t) +{ + std::vector coord_vector{}; + coord_vector.reserve(7); + + const cartesian_layout lyt{{1, 1, 1}}; + + const auto fill_coord_vector = [&cv = coord_vector](const auto& c) { cv.emplace_back(c); }; + + SECTION("With bounds") + { + lyt.foreach_coordinate(fill_coord_vector, {1, 0, 0}, {1, 1, 1}); + + CHECK(coord_vector.size() == 6); + + CHECK(coord_vector[0] == TestType{1, 0, 0}); + + if constexpr (std::is_same_v) + { + CHECK(coord_vector[1] == TestType{0, 0, 1}); + CHECK(coord_vector[2] == TestType{1, 0, 1}); + CHECK(coord_vector[3] == TestType{0, 1, 0}); + CHECK(coord_vector[4] == TestType{1, 1, 0}); + } + else + { + CHECK(coord_vector[1] == TestType{0, 1, 0}); + CHECK(coord_vector[2] == TestType{1, 1, 0}); + CHECK(coord_vector[3] == TestType{0, 0, 1}); + CHECK(coord_vector[4] == TestType{1, 0, 1}); + } + + CHECK(coord_vector[5] == TestType{0, 1, 1}); + } + SECTION("Without bounds") + { + coord_vector.clear(); + coord_vector.reserve(8); + + lyt.foreach_coordinate(fill_coord_vector); + + CHECK(coord_vector.size() == 8); + + CHECK(coord_vector.front().str() == fmt::format("{}", TestType{0, 0, 0})); + CHECK(coord_vector.back().str() == fmt::format("{}", TestType{1, 1, 1})); + } +} + +TEMPLATE_TEST_CASE("Computing area and volume of offset and cubic coordinates", "[coordinates]", offset::ucoord_t, + cube::coord_t) +{ + CHECK(area(TestType{1, 1, 1}) == 4); + CHECK(volume(TestType{1, 1, 1}) == 8); +} + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif