#pragma once #include "base/assert.hpp" #include "base/base.hpp" #include "base/bits.hpp" #include #include #include #include #include namespace m2 { template class CellId { public: // Use enum to avoid linker errors. // Can't realize why static const or constexpr is invalid here ... enum { DEPTH_LEVELS = kDepthLevels }; static uint8_t const MAX_CHILDREN = 4; static uint32_t const MAX_COORD = 1U << DEPTH_LEVELS; CellId() : m_bits(0), m_level(0) { ASSERT(IsValid(), ()); } explicit CellId(std::string const & s) { *this = FromString(s); } static CellId Root() { return CellId(0, 0); } static CellId FromBitsAndLevel(uint64_t bits, int level) { return CellId(bits, level); } static size_t TotalCellsOnLevel(size_t level) { return 1 << (2 * level); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Simple getters int Level() const { ASSERT(IsValid(), (m_bits, m_level)); return m_level; } CellId Parent() const { ASSERT(IsValid(), (m_bits, m_level)); ASSERT_GREATER(m_level, 0, ()); return CellId(m_bits >> 2, m_level - 1); } CellId AncestorAtLevel(int level) const { ASSERT(IsValid(), (m_bits, m_level)); ASSERT_GREATER_OR_EQUAL(m_level, level, ()); return CellId(m_bits >> (2 * (m_level - level)), level); } CellId Child(int8_t c) const { ASSERT(c >= 0 && c < 4, (c, m_bits, m_level)); ASSERT(IsValid(), (m_bits, m_level)); ASSERT_LESS(m_level, DEPTH_LEVELS - 1, ()); return CellId((m_bits << 2) | c, m_level + 1); } char WhichChildOfParent() const { ASSERT(IsValid(), (m_bits, m_level)); ASSERT_GREATER(m_level, 0, ()); return m_bits & 3; } uint64_t SubTreeSize(int depth) const { ASSERT(IsValid(), (m_bits, m_level)); ASSERT(m_level < depth && depth <= DEPTH_LEVELS, (m_bits, m_level, depth)); return TreeSizeForDepth(depth - m_level); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Operators bool operator==(CellId const & cellId) const { ASSERT(IsValid(), (m_bits, m_level)); ASSERT(cellId.IsValid(), (cellId.m_bits, cellId.m_level)); return m_bits == cellId.m_bits && m_level == cellId.m_level; } bool operator!=(CellId const & cellId) const { return !(*this == cellId); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Conversion to/from string std::string ToString() const { ASSERT(IsValid(), (m_bits, m_level)); std::string result(m_level, '0'); uint64_t bits = m_bits; for (int i = 0; i < m_level; ++i, bits >>= 2) result[m_level - 1 - i] += (bits & 3); ASSERT_EQUAL(*this, FromString(result), (m_bits, m_level, result)); return result; } // Tests whether the string |s| is a valid CellId representation. // Note that an empty string is a valid CellId. static bool IsCellId(std::string const & s) { size_t const length = s.size(); if (length >= DEPTH_LEVELS) return false; for (size_t i = 0; i < length; ++i) { if (s[i] < '0' || s[i] > '3') return false; } return true; } static CellId FromString(std::string const & s) { ASSERT(IsCellId(s), (s)); uint64_t bits = 0; size_t const level = s.size(); ASSERT_LESS(level, static_cast(DEPTH_LEVELS), (s)); for (size_t i = 0; i < level; ++i) { ASSERT((s[i] >= '0') && (s[i] <= '3'), (s, i)); bits = (bits << 2) | static_cast(s[i] - '0'); } return CellId(bits, static_cast(level)); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Conversion to/from point // Cell area width and height. // Should be 1 for the bottom level cell. uint32_t Radius() const { ASSERT(IsValid(), (m_bits, m_level)); return 1 << (DEPTH_LEVELS - 1 - m_level); } std::pair XY() const { ASSERT(IsValid(), (m_bits, m_level)); std::pair xy; bits::BitwiseSplit(m_bits, xy.first, xy.second); xy.first = 2 * xy.first + 1; xy.second = 2 * xy.second + 1; xy.first <<= DEPTH_LEVELS - 1 - m_level; xy.second <<= DEPTH_LEVELS - 1 - m_level; ASSERT_EQUAL(*this, FromXY(xy.first, xy.second, m_level), ()); return xy; } static CellId FromXY(uint32_t x, uint32_t y, int level) { ASSERT_LESS(level, static_cast(DEPTH_LEVELS), (x, y, level)); // Since MAX_COORD == 1 << DEPTH_LEVELS, if x|y == MAX_COORD, they should be decremented. if (x >= MAX_COORD) { ASSERT_EQUAL(x, static_cast(MAX_COORD), (x, y, level)); x = MAX_COORD - 1; } if (y >= MAX_COORD) { ASSERT_EQUAL(y, static_cast(MAX_COORD), (x, y, level)); y = MAX_COORD - 1; } x >>= DEPTH_LEVELS - level; y >>= DEPTH_LEVELS - level; uint64_t const bits = bits::BitwiseMerge(x, y); return CellId(bits, level); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Ordering struct LessLevelOrder { bool operator()(CellId const & id1, CellId const & id2) const { if (id1.m_level != id2.m_level) return id1.m_level < id2.m_level; if (id1.m_bits != id2.m_bits) return id1.m_bits < id2.m_bits; return false; } }; struct GreaterLevelOrder { bool operator()(CellId const & id1, CellId const & id2) const { if (id1.m_level != id2.m_level) return id1.m_level > id2.m_level; if (id1.m_bits != id2.m_bits) return id1.m_bits > id2.m_bits; return false; } }; struct LessPreOrder { bool operator()(CellId const & id1, CellId const & id2) const { int64_t const n1 = id1.ToInt64ZOrder(DEPTH_LEVELS); int64_t const n2 = id2.ToInt64ZOrder(DEPTH_LEVELS); ASSERT_EQUAL(n1 < n2, id1.ToString() < id2.ToString(), (id1, id2)); return n1 < n2; } }; ////////////////////////////////////////////////////////////////////////////////////////////////// // Numbering // Default ToInt64(). int64_t ToInt64(int depth) const { return ToInt64ZOrder(depth); } // Default FromInt64(). static CellId FromInt64(int64_t v, int depth) { return FromInt64ZOrder(v, depth); } // Returns the 1-based number this cell would get in the Z-curve ordering (pre-order // traversal of the quadtree) of all the cells in the tree of depth |depth|. // The tree of one vertex is assumed to have depth 1. int64_t ToInt64ZOrder(int depth) const { ASSERT(0 < depth && depth <= DEPTH_LEVELS, (m_bits, m_level, depth)); ASSERT(IsValid(), (m_bits, m_level)); if (m_level >= depth) return AncestorAtLevel(depth - 1).ToInt64ZOrder(depth); uint64_t bits = m_bits; uint64_t res = 0; // When counting, group the nodes by their level. // All nodes to the left of the current one at its level // and, for every ancestor, all nodes to the left of the ancestor // at the ancestor's level will show up earlier during the traversal. // All ancestors are visited before the current node, so add +1 for them. // The result is 1-based, so add +1 for the node itself too. for (int i = 0; i <= m_level; ++i) { res += bits + 1; bits >>= 2; } // By the same reasoning, if the children of every node are ordered // left to right, then all nodes at deeper levels that // are strictly to the left will be visited before the current one. bits = m_bits; for (int i = m_level + 1; i < depth; ++i) { bits <<= 2; res += bits; } ASSERT_GREATER(res, 0, (m_bits, m_level)); ASSERT_LESS_OR_EQUAL(res, TreeSizeForDepth(depth), (m_bits, m_level)); return static_cast(res); } // Returns the CellId with the 1-based number |v| in the Z-curve ordering (pre-order // traversal of the quadtree) of all the cells in the tree of depth |depth|. // The tree of one vertex is assumed to have depth 1. static CellId FromInt64ZOrder(int64_t v, int depth) { ASSERT_GREATER(v, 0, ()); ASSERT(0 < depth && depth <= DEPTH_LEVELS, (v, depth)); ASSERT_LESS_OR_EQUAL(static_cast(v), TreeSizeForDepth(depth), ()); uint64_t bits = 0; int level = 0; while (v > 1) { bits <<= 2; ++level; uint64_t const subtreeSize = TreeSizeForDepth(depth - level); for (--v; static_cast(v) > subtreeSize; v -= subtreeSize) ++bits; } return CellId(bits, level); } private: // Returns the total number of nodes in the tree of depth |depth|. // The tree of one vertex is assumed to have depth 1. // The first few values are 1, 5, 21, 85. static uint64_t TreeSizeForDepth(int depth) { ASSERT(0 < depth && depth <= DEPTH_LEVELS, (depth)); return ((1ULL << 2 * depth) - 1) / 3ULL; } CellId(uint64_t bits, int level) : m_bits(bits), m_level(level) { ASSERT_LESS(level, DEPTH_LEVELS, (bits, level)); ASSERT_LESS(bits, 1ULL << m_level * 2, (bits, m_level)); ASSERT(IsValid(), (m_bits, m_level)); } bool IsValid() const { if (m_level < 0 || m_level >= DEPTH_LEVELS) return false; if (m_bits >= (1ULL << m_level * 2)) return false; return true; } uint64_t m_bits; int m_level; }; template std::string DebugPrint(CellId const & id) { std::ostringstream out; out << "CellId<" << DEPTH_LEVELS << ">(\"" << id.ToString().c_str() << "\")"; return out.str(); } } // namespace m2