Abstract away types and use 64-bit everywhere

We abstract away the types being used from 'int' and 'char'.  The one
exception being for output.

This allows us to experiment to see if using different types can improve
performance.

It turns out it does - and that using 64-bit integers everywhere is a
good idea.
This commit is contained in:
2025-09-03 09:30:33 +02:00
parent 4b9feefbf4
commit 65569982ee

68
main.cc
View File

@@ -13,15 +13,17 @@
#include <iostream>
#include <set>
using size_t = std::size_t;
namespace {
/** (x, y) pair storing a position. */
using Pos = std::pair<int, int>;
using Pos = std::pair<size_t, size_t>;
/** Get x co-ordinate from position */
auto x(Pos const &p) noexcept -> int { return p.first; }
auto x(Pos const &p) noexcept -> size_t { return p.first; }
/** Get y co-ordinate from position */
auto y(Pos const &p) noexcept -> int { return p.second; }
auto y(Pos const &p) noexcept -> size_t { return p.second; }
/** A square - consisting of position of closest corner to origin, and side-length.
*/
@@ -30,7 +32,7 @@ namespace {
* \param pos Position of closest corner to origin
* \param length Side length.
*/
Square(Pos const &pos, int const length) noexcept : pos_(pos), length_(length) {
Square(Pos const &pos, size_t const length) noexcept : pos_(pos), length_(length) {
}
Square(Square const &other) noexcept = default;
@@ -44,23 +46,23 @@ namespace {
~Square() noexcept = default;
/** Get x co-ordinate of closest corner to origin. */
auto x() const noexcept -> int { return ::x(pos_); }
auto x() const noexcept -> size_t { return ::x(pos_); }
/** Get y co-ordinate of closest corner to origin. */
auto y() const noexcept -> int { return ::y(pos_); }
auto y() const noexcept -> size_t { return ::y(pos_); }
/** Get side length. */
auto length() const noexcept -> int { return length_; }
auto length() const noexcept -> size_t { return length_; }
private:
Pos pos_; ///< Position of corner closest to origin
int length_; ///< Side length
size_t length_; ///< Side length
};
/** Structure holding the results.
*/
struct Results {
Results(int length, std::vector<Square> squares) : length_(length), squares_(std::move(squares)) {
Results(size_t length, std::vector<Square> squares) : length_(length), squares_(std::move(squares)) {
}
Results(Results const &other) noexcept = delete;
@@ -69,7 +71,7 @@ namespace {
Results(Results &&other) noexcept = default;
~Results() noexcept = default;
auto length() const noexcept -> int { return length_; }
auto length() const noexcept -> size_t { return length_; }
/** Output the grid. */
auto output() const -> void {
@@ -78,13 +80,13 @@ namespace {
for (auto const& sq : squares_) {
prettify_sq(out, sq);
}
for (auto idx = 0; idx < length_ * length_; idx += length_) {
for (size_t idx = 0; idx < length_ * length_; idx += length_) {
std::cout << std::string_view(out.data() + idx, length_) << '\n';
}
}
private:
auto set(std::string& s, int x, int y, char c) const noexcept -> void {
auto set(std::string& s, size_t x, size_t y, char c) const noexcept -> void {
assert(x < length_);
assert(y < length_);
assert(grid_[x + y * length_] != c);
@@ -106,17 +108,17 @@ namespace {
set(s, sq.x() + n - 1, sq.y(), '+');
set(s, sq.x(), sq.y() + n - 1, '+');
set(s, sq.x() + n - 1, sq.y() + n - 1, '+');
for (int i = 1; i < n - 1; ++i) {
for (size_t i = 1; i < n - 1; ++i) {
set(s, sq.x() + i, sq.y(), '-');
set(s, sq.x() + i, sq.y() + n - 1, '-');
set(s, sq.x(), sq.y() + i, '|');
for (int j = 1; j < n - 1; ++j) {
for (size_t j = 1; j < n - 1; ++j) {
set(s, sq.x() + j, sq.y() + i, ' ');
}
set(s, sq.x() + n - 1, sq.y() + i, '|');
}
int i = sq.x() + n - 1;
size_t i = sq.x() + n - 1;
while (n != 0) {
set(s, --i, sq.y() + 1, '0' + (n % 10));
n /= 10;
@@ -125,14 +127,17 @@ namespace {
}
}
int length_;
size_t length_;
std::vector<Square> squares_;
};
/** An N * N grid of characters. */
struct Grid {
// Type to use for the grid contents
using T = std::int_fast64_t;
/** Construct a grid of given side-length. */
Grid(int length) : grid_(length * length, empty), length_(length) {
Grid(size_t length) : grid_(length * length, empty), length_(length) {
}
Grid(Grid const &other) = delete;
@@ -146,13 +151,13 @@ namespace {
~Grid() noexcept = default;
/** Get grid length */
auto length() const noexcept -> int { return length_; }
auto length() const noexcept -> size_t { return length_; }
/** Add a square to the grid. */
auto add(Square const &sq) noexcept -> void {
for (auto i = sq.x(); i < sq.x() + sq.length(); ++i) {
for (auto j = sq.y(); j < sq.y() + sq.length(); ++j) {
set(i, j, sq.length());
set(i, j, filled);
}
}
}
@@ -168,7 +173,7 @@ namespace {
/** \brief Get length of the largest square that fits at \a pos in the grid.
*/
auto largest_square(Pos const &pos, int n) const noexcept -> int {
auto largest_square(Pos const &pos, size_t n) const noexcept -> size_t {
assert(x(pos) < length_);
assert(y(pos) < length_);
@@ -183,7 +188,7 @@ namespace {
// Make sure we don't go looking in the next row.
auto e = std::min(x(pos) + n, length_);
while (b < e) {
if (grid_[b + y(pos) * length_] != '.') { break; }
if (grid_[b + y(pos) * length_] != empty) { break; }
++b;
}
// Check that this length fits vertically as well.
@@ -195,7 +200,7 @@ namespace {
/** Get the next position to check. n is the size of the square we just
* added.
*/
auto next_pos(Pos const &pos, int n) const noexcept -> Pos {
auto next_pos(Pos const &pos, size_t n) const noexcept -> Pos {
auto const b = grid_.begin() + x(pos) + n + y(pos) * length_;
auto const p = std::find(b, grid_.end(), empty);
auto const v = p - grid_.begin();
@@ -209,30 +214,31 @@ namespace {
* It is an error if (x, y) is already set to c - as that means we have overlapping
* squares.
*/
auto set(int x, int y, char c) noexcept -> void {
auto set(size_t x, size_t y, T c) noexcept -> void {
assert(x < length_);
assert(y < length_);
assert(grid_[x + y * length_] != c);
grid_[x + y * length_] = c;
}
std::vector<char> grid_; ///< The grid
int length_; ///< Side length
std::vector<T> grid_; ///< The grid
size_t length_; ///< Side length
static constexpr char empty = '.'; ///< Character used for an empty cell.
static constexpr char empty = 0; ///< Character used for an empty cell.
static constexpr char filled = 1; ///< Character used for a filled cell,
};
/** Get the n'th triangular number. */
auto triangle_num(int n) noexcept -> int { return (n * (n + 1)) / 2; }
auto triangle_num(size_t n) noexcept -> size_t { return (n * (n + 1)) / 2; }
/** Vector used to identify the available squares. */
using Avail = std::vector<int>;
using Avail = std::vector<size_t>;
/** Find a solution to the \a n th Partridge problem.
*
* Returns the grid of the solution.
*/
auto find_solution(int const n) noexcept -> Results {
auto find_solution(size_t const n) noexcept -> Results {
/* Implementation is iterative, as opposed to recursive.
*
* The recursive implementation is easier to understand - but is
@@ -262,7 +268,7 @@ namespace {
// Start at the origin with a square of longest side length.
Pos pos{0, 0};
int idx = n;
size_t idx = n;
while (true) {
/* If the idx is 0 we've looked at all possible square lengths for this
@@ -305,7 +311,7 @@ namespace {
} // anon namespace
int main(int argc, char **argv) {
auto n = (argc == 1) ? 8 : atoi(argv[1]);
auto n = (argc == 1) ? 8 : std::atol(argv[1]);
auto grid = find_solution(n);
std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n';
grid.output();