Skip to content

Commit cccd019

Browse files
committed
adventofcode/cc/geometry: first iteration of geometry library with a 2D Pos type.
This `Pos` type implements some basic useful stuff: - `+`/`-` operators - string formatting - hashing - Manhattan distance This will certainly be useful enough for most use cases of 2D positions in Advent of Code problems.
1 parent ab8c845 commit cccd019

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
2+
3+
package(default_visibility = ["//adventofcode/cc:__subpackages__"])
4+
5+
cc_library(
6+
name = "pos",
7+
srcs = ["pos.cc"],
8+
hdrs = ["pos.h"],
9+
deps = ["@com_google_absl//absl/strings:str_format"],
10+
)
11+
12+
cc_test(
13+
name = "pos_test",
14+
srcs = ["pos_test.cc"],
15+
deps = [
16+
":pos",
17+
"@com_google_absl//absl/hash:hash_testing",
18+
"@com_google_absl//absl/strings:str_format",
19+
"@com_google_googletest//:gtest_main",
20+
],
21+
)

adventofcode/cc/geometry/pos.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "adventofcode/cc/geometry/pos.h"
2+
3+
#include <cmath>
4+
#include <cstdint>
5+
6+
namespace adventofcode {
7+
namespace cc {
8+
namespace geometry {
9+
int64_t Pos::Distance() const { return Distance(Pos{.x = 0, .y = 0}); }
10+
int64_t Pos::Distance(const Pos& to) const {
11+
return std::abs(x - to.x) + std::abs(y - to.y);
12+
}
13+
} // namespace geometry
14+
} // namespace cc
15+
} // namespace adventofcode

adventofcode/cc/geometry/pos.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef ADVENTOFCODE_CC_GEOMETRY_POS_H
2+
#define ADVENTOFCODE_CC_GEOMETRY_POS_H
3+
4+
#include <cstdint>
5+
#include <ostream>
6+
7+
#include "absl/strings/str_format.h"
8+
9+
namespace adventofcode {
10+
namespace cc {
11+
namespace geometry {
12+
// Pos represents a point with integral coordinates in a 2D plane.
13+
struct Pos {
14+
int64_t x, y;
15+
16+
// Distance returns the Manhattan distance between this Pos and another Pos.
17+
// Calling Distance with no argument returns the Manhattan distance to (0, 0).
18+
// I have not bothered to deal with edge cases like underflows or the distance
19+
// being more than 2^63-1.
20+
int64_t Distance() const;
21+
int64_t Distance(const Pos& to) const;
22+
23+
// AbslStringify implements string formatting for integration with Abseil
24+
// libraries.
25+
template <typename Sink>
26+
friend void AbslStringify(Sink& sink, const Pos& p) {
27+
absl::Format(&sink, "(%d,%d)", p.x, p.y);
28+
}
29+
30+
// GoogleTest _should_ be able to make use of AbslStringify, but for some
31+
// reason it doesn't. However, it does pick up the PrintTo function, as
32+
// explained in
33+
// https://github.com/google/googletest/blob/3288c4deae0710464a0fd21316084c408798b960/docs/advanced.md#teaching-googletest-how-to-print-your-values.
34+
// This implementation of PrintTo simply calls out to the AbslStringify
35+
// function, so that they share the implementation.
36+
friend void PrintTo(const Pos& p, std::ostream* os) { AbslStringify(*os, p); }
37+
38+
// AbslHashValue implements hashing for use with Abseil's hash-based
39+
// containers, like absl::flat_hash_{map,set}.
40+
template <typename H>
41+
friend H AbslHashValue(H h, const Pos& p) {
42+
return H::combine(std::move(h), p.x, p.y);
43+
}
44+
45+
friend bool operator==(const Pos& lhs, const Pos& rhs) {
46+
return lhs.x == rhs.x && lhs.y == rhs.y;
47+
}
48+
friend bool operator!=(const Pos& lhs, const Pos& rhs) {
49+
return !(lhs == rhs);
50+
}
51+
52+
// Arithmetic operators taken from https://stackoverflow.com/a/52377719.
53+
Pos& operator+=(const Pos& rhs) {
54+
x += rhs.x;
55+
y += rhs.y;
56+
return *this;
57+
}
58+
friend Pos operator+(Pos lhs, const Pos& rhs) {
59+
lhs += rhs;
60+
return lhs;
61+
}
62+
63+
Pos& operator-=(const Pos& rhs) {
64+
x -= rhs.x;
65+
y -= rhs.y;
66+
return *this;
67+
}
68+
friend Pos operator-(Pos lhs, const Pos& rhs) {
69+
lhs -= rhs;
70+
return lhs;
71+
}
72+
};
73+
} // namespace geometry
74+
} // namespace cc
75+
} // namespace adventofcode
76+
77+
#endif
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include "adventofcode/cc/geometry/pos.h"
2+
3+
#include "absl/hash/hash_testing.h"
4+
#include "absl/strings/str_format.h"
5+
#include "gtest/gtest.h"
6+
7+
namespace adventofcode {
8+
namespace cc {
9+
namespace geometry {
10+
TEST(PosTest, Distance) {
11+
EXPECT_EQ((Pos{.x = 0, .y = 0}).Distance(), 0);
12+
EXPECT_EQ((Pos{.x = +1, .y = 0}).Distance(), 1);
13+
EXPECT_EQ((Pos{.x = -1, .y = 0}).Distance(), 1);
14+
EXPECT_EQ((Pos{.x = +1, .y = +1}).Distance(), 2);
15+
EXPECT_EQ((Pos{.x = -1, .y = +1}).Distance(), 2);
16+
EXPECT_EQ((Pos{.x = +1, .y = -1}).Distance(), 2);
17+
EXPECT_EQ((Pos{.x = -1, .y = -1}).Distance(), 2);
18+
}
19+
20+
TEST(PosTest, DistanceTo) {
21+
EXPECT_EQ((Pos{.x = 0, .y = 0}).Distance(Pos{.x = 0, .y = 0}), 0);
22+
EXPECT_EQ((Pos{.x = +1, .y = 0}).Distance(Pos{.x = +1, .y = 0}), 0);
23+
EXPECT_EQ((Pos{.x = -1, .y = 0}).Distance(Pos{.x = +1, .y = 0}), 2);
24+
EXPECT_EQ((Pos{.x = -1, .y = -1}).Distance(Pos{.x = +1, .y = +1}), 4);
25+
}
26+
27+
TEST(PosTest, AbslStringify) {
28+
// Using absl::StrFormat here is my lazy version of calling the AbslStringify
29+
// function.
30+
EXPECT_EQ(absl::StrFormat("%v", Pos{.x = +1, .y = +1}), "(1,1)");
31+
EXPECT_EQ(absl::StrFormat("%v", Pos{.x = +1, .y = -1}), "(1,-1)");
32+
EXPECT_EQ(absl::StrFormat("%v", Pos{.x = -1, .y = +1}), "(-1,1)");
33+
EXPECT_EQ(absl::StrFormat("%v", Pos{.x = -1, .y = -1}), "(-1,-1)");
34+
}
35+
36+
TEST(PosTest, AbslHashValue) {
37+
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
38+
Pos{.x = 0, .y = 0},
39+
Pos{.x = +1, .y = +1},
40+
Pos{.x = +1, .y = -1},
41+
Pos{.x = -1, .y = +1},
42+
Pos{.x = -1, .y = -1},
43+
Pos{.x = -123, .y = +456},
44+
}));
45+
}
46+
47+
TEST(PosTest, Addition) {
48+
Pos a{.x = 1, .y = 2};
49+
Pos b{.x = 10, .y = 10};
50+
Pos want{.x = 11, .y = 12};
51+
EXPECT_EQ(a + b, want);
52+
EXPECT_EQ(b + a, want);
53+
Pos a2 = a;
54+
a2 += b;
55+
EXPECT_EQ(a2, want);
56+
Pos b2 = b;
57+
b2 += a;
58+
EXPECT_EQ(b2, want);
59+
}
60+
61+
TEST(PosTest, Subtraction) {
62+
Pos a{.x = 1, .y = 2};
63+
Pos b{.x = 10, .y = 10};
64+
Pos amb{.x = -9, .y = -8};
65+
Pos bma{.x = 9, .y = 8};
66+
EXPECT_EQ(a - b, amb);
67+
EXPECT_EQ(b - a, bma);
68+
Pos a2 = a;
69+
a2 -= b;
70+
EXPECT_EQ(a2, amb);
71+
Pos b2 = b;
72+
b2 -= a;
73+
EXPECT_EQ(b2, bma);
74+
}
75+
} // namespace geometry
76+
} // namespace cc
77+
} // namespace adventofcode

0 commit comments

Comments
 (0)