Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
hash.hpp
Go to the documentation of this file.
1#pragma once
2
4
12
13#include <cstddef>
14#include <cstdint>
15#include <functional>
16#include <type_traits>
17#include "forward.hpp"
18#include "numeric.hpp"
19
20namespace pgl::detail {
21
22template <class T>
23inline void hashCombine(std::size_t& seed, const T& value) {
24#if defined(__SIZEOF_INT128__)
25 // libstdc++ does not provide std::hash for the 128-bit integer extension
26 // types under strict ISO modes (-std=c++NN), so hash their two 64-bit
27 // halves instead of relying on std::hash<__int128>.
28 if constexpr (std::is_same_v<T, __int128_t> || std::is_same_v<T, __uint128_t>) {
29 const auto bits = static_cast<__uint128_t>(value);
30 hashCombine(seed, static_cast<std::uint64_t>(bits));
31 hashCombine(seed, static_cast<std::uint64_t>(bits >> 64));
32 } else
33#endif
34 {
35 seed ^= std::hash<T>{}(value) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
36 }
37}
38
39} // namespace pgl::detail
40
41
42namespace std {
43
47 template <class Number, class Label>
48 struct hash<pgl::Point<Number, Label>> {
49 std::size_t operator()(const pgl::Point<Number, Label>& point) const {
50 std::size_t seed = pgl::detail::shapeRank<pgl::Point<Number, Label>>;
51 pgl::detail::hashCombine(seed, point.x());
52 pgl::detail::hashCombine(seed, point.y());
53 return seed;
54 }
55 };
56
57
61 template <class Int>
62 struct hash<pgl::Rational<Int>> {
63 size_t operator()(const pgl::Rational<Int>& r) const noexcept {
64 // Equal values must hash equally, and a Rational may hold its value
65 // unreduced, so the hash has to be taken over the reduced parts.
66 // Simplify once and read both from that: asking the value itself for
67 // numerator() and denominator() would run the same gcd twice.
68 const pgl::Rational<Int> reduced = r.simplified();
69 std::size_t seed = 1;
70 pgl::detail::hashCombine(seed, reduced.numerator());
71 pgl::detail::hashCombine(seed, reduced.denominator());
72 return seed;
73 }
74 };
75
79 template <>
80 struct hash<pgl::BigInt> {
81 std::size_t operator()(const pgl::BigInt& b) const noexcept {
82 std::size_t seed = 15;
83 pgl::detail::hashCombine(seed, b.negative_);
84 pgl::detail::hashCombine(seed, b.small_);
85 for (const auto& limb : b.limbs_) {
86 pgl::detail::hashCombine(seed, limb);
87 }
88 return seed;
89 }
90 };
91
95 template <class PointType, class LabelType>
96 struct hash<pgl::Segment<PointType, LabelType>> {
97 std::size_t operator()(const pgl::Segment<PointType, LabelType>& segment) const {
98 std::size_t seed = pgl::detail::shapeRank<pgl::Segment<PointType, LabelType>>;
99 pgl::detail::hashCombine(seed, segment.min());
100 pgl::detail::hashCombine(seed, segment.max());
101 return seed;
102 }
103 };
104
108 template <class PointType, class LabelType>
109 struct hash<pgl::OrientedSegment<PointType, LabelType>> {
110 std::size_t operator()(const pgl::OrientedSegment<PointType, LabelType>& segment) const {
111 std::size_t seed = pgl::detail::shapeRank<pgl::OrientedSegment<PointType, LabelType>>;
112 pgl::detail::hashCombine(seed, segment.source());
113 pgl::detail::hashCombine(seed, segment.target());
114 return seed;
115 }
116 };
117
118
122 template <class PointType, class LabelType>
123 struct hash<pgl::Line<PointType, LabelType>> {
124 std::size_t operator()(const pgl::Line<PointType, LabelType>& line) const {
125 std::size_t seed = pgl::detail::shapeRank<pgl::Line<PointType, LabelType>>;
126 if (line.isDegenerate()) {
127 pgl::detail::hashCombine(seed, line.min());
128 }
129 else if (line.isVertical()) {
130 pgl::detail::hashCombine(seed, line.min().x());
131 }
132 else {
133 using N = typename pgl::Line<PointType>::NumberType;
134 if constexpr (pgl::is_Rational_v<N>) {
135 // Coordinates are already exact rationals, so the dual
136 // slope/intercept are rationals too: divide and hash the
137 // reduced values directly (a nested Rational<Rational> is
138 // neither needed nor well-formed).
139 const auto [anum,bnum,den] = line.template dualCoordinates<N>();
140 pgl::detail::hashCombine(seed, anum / den);
141 pgl::detail::hashCombine(seed, bnum / den);
142 }
143 else {
144 // Match the precision used by Line::operator<=> (see
145 // predicates.hpp): dualCoordinates computes bnum = anum*px - py*den,
146 // a product of two coordinate-magnitude values, which overflows at
147 // the input width. Promote so equal lines hash equal.
148 using IntRational = pgl::detail::promoted_number_t<N>;
149 using RationalKey = pgl::Rational<IntRational>;
150 const auto [anum,bnum,den] = line.template dualCoordinates<IntRational>();
151
152 pgl::detail::hashCombine(seed, RationalKey(anum,den));
153 pgl::detail::hashCombine(seed, RationalKey(bnum,den));
154 }
155 }
156 return seed;
157 }
158 };
159
160
164 template <class PointType, class LabelType>
165 struct hash<pgl::OrientedLine<PointType, LabelType>> {
166 std::size_t operator()(const pgl::OrientedLine<PointType, LabelType>& line) const {
167 std::size_t seed = pgl::detail::shapeRank<pgl::OrientedLine<PointType, LabelType>>
168 + (line.source() < line.target() ? 0u : 1u);
169 using T = pgl::Line<PointType>;
170 pgl::detail::hashCombine(seed, static_cast<T>(line));
171 return seed;
172 }
173 };
174
175
179 template <class PointType, class LabelType>
180 struct hash<pgl::Ray<PointType, LabelType>> {
181 std::size_t operator()(const pgl::Ray<PointType, LabelType>& ray) const {
182 std::size_t seed = pgl::detail::shapeRank<pgl::Ray<PointType, LabelType>>;
183 pgl::detail::hashCombine(seed, ray.source());
184 using T = pgl::Line<PointType>;
185 pgl::detail::hashCombine(seed, static_cast<T>(ray));
186 return seed;
187 }
188 };
189
193 template <class PointType, class LabelType>
194 struct hash<pgl::Halfplane<PointType, LabelType>> {
195 std::size_t operator()(const pgl::Halfplane<PointType, LabelType>& halfplane) const {
196 std::size_t seed = pgl::detail::shapeRank<pgl::Halfplane<PointType, LabelType>>
197 + (halfplane.source() < halfplane.target() ? 0u : 1u);
198 using T = pgl::Line<PointType>;
199 pgl::detail::hashCombine(seed, static_cast<T>(halfplane));
200 return seed;
201 }
202 };
203
207 template <class PointType, class LabelType>
208 struct hash<pgl::Rectangle<PointType, LabelType>> {
209 std::size_t operator()(const pgl::Rectangle<PointType, LabelType>& rectangle) const {
210 std::size_t seed = pgl::detail::shapeRank<pgl::Rectangle<PointType, LabelType>>;
211 pgl::detail::hashCombine(seed, rectangle.min());
212 pgl::detail::hashCombine(seed, rectangle.max());
213 return seed;
214 }
215 };
216
220 template <class PointType, class LabelType>
221 struct hash<pgl::Triangle<PointType, LabelType>> {
222 std::size_t operator()(const pgl::Triangle<PointType, LabelType>& triangle) const {
223 std::size_t seed = pgl::detail::shapeRank<pgl::Triangle<PointType, LabelType>>;
224 pgl::detail::hashCombine(seed, triangle.a());
225 pgl::detail::hashCombine(seed, triangle.b());
226 pgl::detail::hashCombine(seed, triangle.c());
227 return seed;
228 }
229 };
230
234 template <class PointType, class LabelType>
235 struct hash<pgl::Disk<PointType, LabelType>> {
236 std::size_t operator()(const pgl::Disk<PointType, LabelType>& disk) const {
237 std::size_t seed = pgl::detail::shapeRank<pgl::Disk<PointType, LabelType>>;
238 pgl::detail::hashCombine(seed, disk.isDegenerate());
239 if (disk.isDegenerate()) {
240 pgl::detail::hashCombine(seed, disk.a());
241 pgl::detail::hashCombine(seed, disk.b());
242 pgl::detail::hashCombine(seed, disk.c());
243 } else {
244 using Number = typename std::decay_t<decltype(disk)>::NumberType;
245 pgl::detail::hashCombine(seed, disk.template center<Number>());
246 pgl::detail::hashCombine(seed, disk.template squaredRadius<Number>());
247 }
248 return seed;
249 }
250 };
251
255 template <class PointType, class LabelType>
256 struct hash<pgl::Convex<PointType, LabelType>> {
257 std::size_t operator()(const pgl::Convex<PointType, LabelType>& convex) const {
259 if (convex.hash_ != Shape::hashUnset_) {
260 return convex.hash_;
261 }
262 std::size_t seed = pgl::detail::shapeRank<Shape>;
263 for (const auto& vertex : convex) {
264 pgl::detail::hashCombine(seed, vertex);
265 }
266 // Never store the sentinel: remap the single colliding value so the
267 // cache can always distinguish "computed" from "not computed".
268 if (seed == Shape::hashUnset_) {
269 seed = Shape::hashUnset_ - 1;
270 }
271 convex.hash_ = seed;
272 return seed;
273 }
274 };
275
279 template <class PointType, class LabelType>
280 struct hash<pgl::Polygon<PointType, LabelType>> {
281 std::size_t operator()(const pgl::Polygon<PointType, LabelType>& polygon) const {
283 if (polygon.hash_ != Shape::hashUnset_) {
284 return polygon.hash_;
285 }
286 std::size_t seed = pgl::detail::shapeRank<Shape>;
287 for (const auto& vertex : polygon) {
288 pgl::detail::hashCombine(seed, vertex);
289 }
290 // Never store the sentinel: remap the single colliding value so the
291 // cache can always distinguish "computed" from "not computed".
292 if (seed == Shape::hashUnset_) {
293 seed = Shape::hashUnset_ - 1;
294 }
295 polygon.hash_ = seed;
296 return seed;
297 }
298 };
299
306 template <class PointType, class LabelType>
307 struct hash<pgl::PolygonWithHoles<PointType, LabelType>> {
308 std::size_t operator()(const pgl::PolygonWithHoles<PointType, LabelType>& region) const {
310 if (region.hash_ != Shape::hashUnset_) {
311 return region.hash_;
312 }
313 std::size_t seed = pgl::detail::shapeRank<Shape>;
314 pgl::detail::hashCombine(seed, region.outer());
315 for (const auto& hole : region.holes()) {
316 pgl::detail::hashCombine(seed, hole);
317 }
318 // Never store the sentinel: remap the single colliding value so the
319 // cache can always distinguish "computed" from "not computed".
320 if (seed == Shape::hashUnset_) {
321 seed = Shape::hashUnset_ - 1;
322 }
323 region.hash_ = seed;
324 return seed;
325 }
326 };
327
334 template <class PointType, class LabelType>
335 struct hash<pgl::PolygonSet<PointType, LabelType>> {
336 std::size_t operator()(const pgl::PolygonSet<PointType, LabelType>& set) const {
338 if (set.hash_ != Shape::hashUnset_) {
339 return set.hash_;
340 }
341 std::size_t seed = pgl::detail::shapeRank<Shape>;
342 for (const auto& component : set.components()) {
343 pgl::detail::hashCombine(seed, component);
344 }
345 // Never store the sentinel: remap the single colliding value so the
346 // cache can always distinguish "computed" from "not computed".
347 if (seed == Shape::hashUnset_) {
348 seed = Shape::hashUnset_ - 1;
349 }
350 set.hash_ = seed;
351 return seed;
352 }
353 };
354
358 template <class PointType, class LabelType>
359 struct hash<pgl::HalfplaneIntersection<PointType, LabelType>> {
360 std::size_t operator()(const pgl::HalfplaneIntersection<PointType, LabelType>& region) const {
362 if (region.hash_ != Shape::hashUnset_) {
363 return region.hash_;
364 }
365 std::size_t seed = pgl::detail::shapeRank<Shape>;
366 pgl::detail::hashCombine(seed, region.empty());
367 for (const auto& halfplane : region) {
368 pgl::detail::hashCombine(seed, halfplane);
369 }
370 // Never store the sentinel: remap the single colliding value so the
371 // cache can always distinguish "computed" from "not computed".
372 if (seed == Shape::hashUnset_) {
373 seed = Shape::hashUnset_ - 1;
374 }
375 region.hash_ = seed;
376 return seed;
377 }
378 };
379
383 template <class PointType, class LabelType, class Storage>
384 struct hash<pgl::MonotoneChain<PointType, LabelType, Storage>> {
385 std::size_t operator()(const pgl::MonotoneChain<PointType, LabelType, Storage>& chain) const {
387 if (chain.hash_ != Shape::hashUnset_) {
388 return chain.hash_;
389 }
390 std::size_t seed = pgl::detail::shapeRank<Shape>;
391 for (const auto& vertex : chain) {
392 pgl::detail::hashCombine(seed, vertex);
393 }
394 // Never store the sentinel: remap the single colliding value so the
395 // cache can always distinguish "computed" from "not computed".
396 if (seed == Shape::hashUnset_) {
397 seed = Shape::hashUnset_ - 1;
398 }
399 chain.hash_ = seed;
400 return seed;
401 }
402 };
403
407 template <class PointType, class LabelType>
408 struct hash<pgl::Polyline<PointType, LabelType>> {
409 std::size_t operator()(const pgl::Polyline<PointType, LabelType>& polyline) const {
411 if (polyline.hash_ != Shape::hashUnset_) {
412 return polyline.hash_;
413 }
414 std::size_t seed = pgl::detail::shapeRank<Shape>;
415 // A polyline equals its reversal, so the vertices are hashed in
416 // canonical direction rather than in the stored traversal order.
417 const bool reversed = !polyline.storedIsCanonical();
418 for (std::size_t i = 0; i < polyline.size(); ++i) {
419 pgl::detail::hashCombine(seed, polyline.canonicalAt(i, reversed));
420 }
421 // Never store the sentinel: remap the single colliding value so the
422 // cache can always distinguish "computed" from "not computed".
423 if (seed == Shape::hashUnset_) {
424 seed = Shape::hashUnset_ - 1;
425 }
426 polyline.hash_ = seed;
427 return seed;
428 }
429 };
430
434 template <class PointType>
435 struct hash<pgl::EmptyShape<PointType>> {
436 std::size_t operator()(const pgl::EmptyShape<PointType>&) const {
437 return pgl::detail::shapeRank<pgl::EmptyShape<PointType>>;
438 }
439 };
440
444 template <class PointType>
445 struct hash<pgl::Shape<PointType>> {
446 std::size_t operator()(const pgl::Shape<PointType>& shape) const {
447 std::size_t seed = 12;
448 pgl::detail::hashCombine(seed, shape.variant().index());
449 std::visit(
450 [&seed](const auto& value) {
451 pgl::detail::hashCombine(seed, value);
452 },
453 shape.variant());
454 return seed;
455 }
456 };
457
458}//std
Lightweight SVG canvas for drawing Pangolin shapes.
constexpr Int numerator() const noexcept
Get numerator (in lowest terms).
Definition rational.hpp:359
constexpr Int denominator() const noexcept
Get denominator (in lowest terms).
Definition rational.hpp:365
Forward declarations for core numeric and geometry types.
HalfplaneIntersection() -> HalfplaneIntersection< Point<>, NoLabel >
Definition halfplaneintersection.hpp:2308
Rectangle() -> Rectangle< Point<>, NoLabel >
Definition rectangle.hpp:2384
constexpr bool is_Rational_v
Definition rational.hpp:37
Line() -> Line< Point<>, NoLabel >
Point() -> Point< int >
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
OrientedSegment() -> OrientedSegment< Point<>, NoLabel >
Rational(T) -> Rational< T >
MonotoneChain() -> MonotoneChain< Point<>, NoLabel >
Definition monotonechain.hpp:2439
Shape(const std::variant< T, Ts... > &) -> Shape< detail::shape_point_type_t< T > >
PolygonWithHoles() -> PolygonWithHoles< Point<>, NoLabel >
Definition polygonwithholes.hpp:3093
Convex() -> Convex< Point<>, NoLabel >
Definition convex.hpp:3311
Segment() -> Segment< Point<>, NoLabel >
Halfplane() -> Halfplane< Point<>, NoLabel >
Polyline() -> Polyline< Point<>, NoLabel >
Definition polyline.hpp:2369
Ray() -> Ray< Point<>, NoLabel >
Polygon() -> Polygon< Point<>, NoLabel >
Definition polygon.hpp:3200
Disk() -> Disk< Point<>, NoLabel >
Deduces a default disk with Point<> boundary points and no label.
Definition disk.hpp:1691
OrientedLine() -> OrientedLine< Point<>, NoLabel >
Triangle() -> Triangle< Point<>, NoLabel >
Definition triangle.hpp:2029
Numeric concepts and helpers shared by exact geometry operations.
constexpr bool isDegenerate() const
Returns whether the three boundary points are collinear.
Definition disk.hpp:348
constexpr const PointType & c() const
Returns the third boundary point in canonical order.
Definition disk.hpp:244
constexpr const PointType & a() const
Returns the first boundary point (lexicographically smallest).
Definition disk.hpp:228
constexpr const PointType & b() const
Returns the second boundary point in canonical order.
Definition disk.hpp:235
constexpr bool empty() const
Returns whether the region is the empty set.
Definition halfplaneintersection.hpp:649
constexpr const PointType & target() const
Returns the target boundary point.
Definition halfplane.hpp:193
constexpr const PointType & source() const
Returns the source boundary point.
Definition halfplane.hpp:181
constexpr bool isVertical() const
Returns whether the line is vertical.
Definition predicates.hpp:461
constexpr const PointType & min() const
Returns the smallest stored defining point.
Definition line.hpp:180
PointType::NumberType NumberType
Definition line.hpp:54
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:451
constexpr const PointType & target() const
Returns the target defining point.
Definition orientedline.hpp:195
constexpr const PointType & source() const
Returns the source defining point.
Definition orientedline.hpp:183
constexpr const PointType & source() const
Returns the source endpoint.
Definition orientedsegment.hpp:178
constexpr const PointType & target() const
Returns the target endpoint.
Definition orientedsegment.hpp:190
constexpr const NumberType & x() const
Returns the x coordinate.
Definition point.hpp:193
constexpr const NumberType & y() const
Returns the y coordinate.
Definition point.hpp:205
constexpr const std::vector< ComponentType > & components() const
Returns the components in canonical order.
Definition polygonset.hpp:277
constexpr const PolygonType & outer() const
Returns the outer boundary.
Definition polygonwithholes.hpp:178
constexpr const std::vector< PolygonType > & holes() const
Returns the holes in canonical order.
Definition polygonwithholes.hpp:202
constexpr std::size_t size() const
Returns the number of vertices in the polyline.
Definition polyline.hpp:388
constexpr const PointType & source() const
Returns the source point of the ray.
Definition ray.hpp:181
constexpr const PointType & min() const
Returns the minimum corner (min x, min y).
Definition rectangle.hpp:347
constexpr const PointType & max() const
Returns the maximum corner (max x, max y).
Definition rectangle.hpp:359
constexpr const PointType & max() const
Returns the largest stored endpoint.
Definition segment.hpp:199
constexpr const PointType & min() const
Returns the smallest stored endpoint.
Definition segment.hpp:190
constexpr const Variant & variant() const
Returns the underlying variant.
Definition shape.hpp:264
constexpr const PointType & b() const
Returns the second vertex.
Definition triangle.hpp:217
constexpr const PointType & a() const
Returns the first vertex.
Definition triangle.hpp:208
constexpr const PointType & c() const
Returns the third vertex.
Definition triangle.hpp:226