Turi Create  4.0
hilbert_curve.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_SGRAPH_HILBERT_CURVE_HPP
7 #define TURI_SGRAPH_HILBERT_CURVE_HPP
8 #include <utility>
9 #include <cstdint>
10 #include <cstddef>
11 #include <core/logging/assertions.hpp>
12 #include <core/util/bitops.hpp>
13 
14 namespace turi {
15 
16 
17 /**
18  * \internal
19  * \ingroup sgraph_physical
20  * \addtogroup sgraph_compute SGraph Compute
21  * \{
22  */
23 
24 /**
25  * For an n*n square and a hilbert index (s) ranging from 0 to n*n-1, this
26  * function returns the coordinate of the s^th position along the hilbert
27  * curve. n must be at least 2 and must be a power of 2.
28  *
29  * Algorithm from Figure 14-8 in Hacker's Delight.
30  */
31 inline std::pair<size_t, size_t> hilbert_index_to_coordinate(size_t s, size_t n) {
32  if (s == 0 && n == 1) return {0, 0};
33 
34  ASSERT_GE(n, 2);
36  n = n_trailing_zeros(n); // convert to the "Order" of the curve. i.e. log(n)
37  size_t comp, swap, cs, t, sr;
38 
39  s = s | (0x55555555 << 2*n); // Pad s on left with 01
40  sr = (s >> 1) & 0x55555555; // (no change) groups.
41  cs = ((s & 0x55555555) + sr) // Compute complement &
42  ^ 0x55555555; // swap info in two-bit
43  // groups.
44  // Parallel prefix xor op to propagate both complement
45  // and swap info together from left to right (there is
46  // no step "cs ^= cs >> 1", so in effect it computes
47  // two independent parallel prefix operations on two
48  // interleaved sets of sixteen bits).
49 
50  cs = cs ^ (cs >> 2);
51  cs = cs ^ (cs >> 4);
52  cs = cs ^ (cs >> 8);
53  cs = cs ^ (cs >> 16);
54  swap = cs & 0x55555555; // Separate the swap and
55  comp = (cs >> 1) & 0x55555555; // complement bits.
56 
57  t = (s & swap) ^ comp; // Calculate x and y in
58  s = s ^ sr ^ t ^ (t << 1); // the odd & even bit
59  // positions, resp.
60  s = s & ((1 << 2*n) - 1); // Clear out any junk
61  // on the left (unpad).
62 
63  // Now "unshuffle" to separate the x and y bits.
64 
65  t = (s ^ (s >> 1)) & 0x22222222; s = s ^ t ^ (t << 1);
66  t = (s ^ (s >> 2)) & 0x0C0C0C0C; s = s ^ t ^ (t << 2);
67  t = (s ^ (s >> 4)) & 0x00F000F0; s = s ^ t ^ (t << 4);
68  t = (s ^ (s >> 8)) & 0x0000FF00; s = s ^ t ^ (t << 8);
69  return {s >> 16, s & 0xFFFF};
70 }
71 
72 
73 /**
74  * For an n*n square and a coordinate within the square. Returns the
75  * hilbert index which is the position of the coordinate along the hilbert curve.
76  * n must be at least 2 and must be a power of 2.
77  *
78  * Algorithm from Figure 14-9 in Hacker's Delight.
79  */
80 inline size_t coordinate_to_hilbert_index(std::pair<size_t, size_t> coord, size_t n) {
81  if ((n == 1) && (coord.first == 0) && (coord.second = 0)) return 0;
82 
83  ASSERT_GE(n, 2);
85  ASSERT_LT(coord.first, n);
86  ASSERT_LT(coord.second, n);
87 
88  n = n_trailing_zeros(n); // convert to the "Order" of the curve. i.e. log(n)
89  size_t i;
90  size_t state, s, row;
91  size_t x = coord.first;
92  size_t y = coord.second;
93 
94  state = 0; // Initialize.
95  s = 0;
96  for (i = n - 1; i >= 0; i--) {
97  row = 4*state | 2*((x >> i) & 1) | ((y >> i) & 1);
98  s = (s << 2) | ((0x361E9CB4 >> 2*row) & 3);
99  state = (0x8FE65831 >> 2*row) & 3;
100  }
101  return s;
102 }
103 
104 /// \}
105 } // namespace turi
106 #endif // TURI_SGRAPH_ZORDER_HPP
static unsigned int n_trailing_zeros(T v, _ENABLE_IF_UINT(T))
Definition: bitops.hpp:206
#define ASSERT_TRUE(cond)
Definition: assertions.hpp:309
static bool is_power_of_2(const T &x, _ENABLE_IF_UINT(T))
Definition: bitops.hpp:52
size_t coordinate_to_hilbert_index(std::pair< size_t, size_t > coord, size_t n)
std::pair< size_t, size_t > hilbert_index_to_coordinate(size_t s, size_t n)