From f1efd73fe689d8fbceff7b453567f2660f554aaf Mon Sep 17 00:00:00 2001 From: Yury Melnichek Date: Sun, 11 Sep 2011 12:41:57 +0200 Subject: [PATCH] CellId refactoring: introduce depth variable and initialise it with DEPTH_LEVELS by default; rename few names and rearrange the code; CelId::IsCellId("") now returns true. --- geometry/cellid.hpp | 242 ++++++++++++---------- geometry/covering.hpp | 20 +- geometry/geometry_tests/cellid_test.cpp | 6 +- geometry/geometry_tests/covering_test.cpp | 2 +- 4 files changed, 149 insertions(+), 121 deletions(-) diff --git a/geometry/cellid.hpp b/geometry/cellid.hpp index c8b9afb00e..20acd6621e 100644 --- a/geometry/cellid.hpp +++ b/geometry/cellid.hpp @@ -12,6 +12,7 @@ namespace m2 template class CellId { public: + // TODO: Move CellId::DEPTH_LEVELS to private. static int const DEPTH_LEVELS = kDepthLevels; static uint32_t const MAX_COORD = 1U << DEPTH_LEVELS; @@ -25,30 +26,6 @@ public: *this = FromString(s); } - pair ToBitsAndLevel() const - { - ASSERT(IsValid(), (m_Bits, m_Level)); - return pair(m_Bits, m_Level); - } - - // Only some number of lowest bits are used. - int64_t ToInt64(int depth = DEPTH_LEVELS) const - { - ASSERT(IsValid(), (m_Bits, m_Level)); - uint64_t bits = m_Bits, res = 0; - for (int i = 0; i <= m_Level; ++i, bits >>= 2) - res += bits + 1; - 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, SubTreeSize(depth - 1), (m_Bits, m_Level)); - return static_cast(res); - } - static CellId Root() { return CellId(0, 0); @@ -59,59 +36,13 @@ public: return CellId(bits, level); } - static bool IsCellId(string const & s) - { - size_t const length = s.size(); - if (length == 0) - return false; - for (size_t i = 0; i < length; ++i) - { - if (s[i] < '0' || s[i] > '3') - return false; - } - return true; - } + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Simple getters - static CellId FromString(string const & 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) | (s[i] - '0'); - } - return CellId(bits, static_cast(level)); - } - - static CellId FromInt64(int64_t v, int depth = DEPTH_LEVELS) - { - ASSERT_GREATER(v, 0, ()); - ASSERT_LESS_OR_EQUAL(v, SubTreeSize(depth - 1), ()); - uint64_t bits = 0; - int level = 0; - --v; - while (v > 0) - { - bits <<= 2; - ++level; - uint64_t subTreeSize = SubTreeSize(depth - 1 - level); - for (--v; v >= subTreeSize; v -= subTreeSize) - ++bits; - } - return CellId(bits, level); - } - - string ToString() const + int Level() const { ASSERT(IsValid(), (m_Bits, m_Level)); - 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; + return m_Level; } CellId Parent() const @@ -143,18 +74,82 @@ public: return m_Bits & 3; } - inline bool operator == (CellId const & cellId) const + uint64_t SubTreeSize(int depth = DEPTH_LEVELS) const + { + ASSERT(IsValid(), (m_Bits, m_Level)); + ASSERT_LESS(m_Level, depth, (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; } - inline bool operator != (CellId const & cellId) const + bool operator != (CellId const & cellId) const { return !(*this == cellId); } + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Conversion to/from string + + string ToString() const + { + ASSERT(IsValid(), (m_Bits, m_Level)); + 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; + } + + // Is string @s a valid CellId representation? + // Note that empty string is a valid CellId. + static bool IsCellId(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(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) | (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); + } + pair XY() const { ASSERT(IsValid(), (m_Bits, m_Level)); @@ -174,18 +169,6 @@ public: return xy; } - uint32_t Radius() const - { - ASSERT(IsValid(), (m_Bits, m_Level)); - return 1 << (DEPTH_LEVELS - 1 - m_Level); - } - - int Level() const - { - ASSERT(IsValid(), (m_Bits, m_Level)); - return m_Level; - } - static CellId FromXY(uint32_t x, uint32_t y, int level = DEPTH_LEVELS - 1) { ASSERT_LESS(level, static_cast(DEPTH_LEVELS), (x, y, level)); @@ -209,13 +192,10 @@ public: return CellId(bits, level); } - uint64_t SubTreeSize() const - { - ASSERT(IsValid(), (m_Bits, m_Level)); - return ((1ULL << 2 * (DEPTH_LEVELS - m_Level)) - 1) / 3ULL; - } + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Ordering - struct LessQueueOrder + struct LessLevelOrder { bool operator() (CellId const & id1, CellId const & id2) const { @@ -227,28 +207,76 @@ public: } }; - struct LessStackOrder + struct LessPreOrder { bool operator() (CellId const & id1, CellId const & id2) const { - return id1.ToString() < id2.ToString(); + int64_t const n1 = id1.ToInt64LevelZOrder(DEPTH_LEVELS); + int64_t const n2 = id2.ToInt64LevelZOrder(DEPTH_LEVELS); + ASSERT_EQUAL(n1 < n2, id1.ToString() < id2.ToString(), (id1, id2)); + return n1 < n2; } }; + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Numbering + + // Default ToInt64(). + int64_t ToInt64(int depth = DEPTH_LEVELS) const + { + return ToInt64LevelZOrder(depth); + } + + // Default FromInt64(). + static CellId FromInt64(int64_t v, int depth = DEPTH_LEVELS) + { + return FromInt64LevelZOrder(v, depth); + } + + // Level order, numbering by Z-curve. + int64_t ToInt64LevelZOrder(int depth) const + { + ASSERT(IsValid(), (m_Bits, m_Level)); + uint64_t bits = m_Bits, res = 0; + for (int i = 0; i <= m_Level; ++i, bits >>= 2) + res += bits + 1; + 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); + } + + // Level order, numbering by Z-curve. + static CellId FromInt64LevelZOrder(int64_t v, int depth) + { + ASSERT_GREATER(v, 0, ()); + ASSERT_LESS_OR_EQUAL(v, TreeSizeForDepth(depth), ()); + uint64_t bits = 0; + int level = 0; + --v; + while (v > 0) + { + bits <<= 2; + ++level; + uint64_t subTreeSize = TreeSizeForDepth(depth - level); + for (--v; v >= subTreeSize; v -= subTreeSize) + ++bits; + } + return CellId(bits, level); + } + +//////////////////////////////////////////////////////////////////////////////////////////////////// private: - inline static uint64_t SubTreeSize(int level) + static uint64_t TreeSizeForDepth(int depth) { - ASSERT_GREATER_OR_EQUAL(level, 0, ()); - static uint64_t const kSubTreeSize[] = - { - 1LL,5LL,21LL,85LL,341LL,1365LL,5461LL,21845LL,87381LL,349525LL,1398101LL,5592405LL,22369621LL, - 89478485LL,357913941LL,1431655765LL,5726623061LL,22906492245LL,91625968981LL,366503875925LL, - 1466015503701LL,5864062014805LL,23456248059221LL,93824992236885LL,375299968947541LL, - 1501199875790165LL,6004799503160661LL,24019198012642645LL,96076792050570581LL, - 384307168202282325LL,1537228672809129301LL,6148914691236517205LL - }; - return kSubTreeSize[level]; + ASSERT_GREATER(depth, 0, ()); + return ((1ULL << 2 * depth) - 1) / 3ULL; } CellId(uint64_t bits, int level) : m_Bits(bits), m_Level(level) diff --git a/geometry/covering.hpp b/geometry/covering.hpp index 1a78b94ad3..0d2174b6cd 100644 --- a/geometry/covering.hpp +++ b/geometry/covering.hpp @@ -23,7 +23,7 @@ class Covering { public: typedef CellIdT CellId; - typedef typename CellId::LessQueueOrder LessQueueOrder; + typedef typename CellId::LessLevelOrder LessLevelOrder; Covering() : m_Size(0) {} @@ -112,7 +112,7 @@ private: void SimplifyLevel(int level) { - map parentCellCounts; + map parentCellCounts; typedef typename vector::const_iterator ConstIteartor; for (ConstIteartor it = m_Covering[level].begin(); it != m_Covering[level].end(); ++it) ++parentCellCounts[it->Parent()]; @@ -125,8 +125,8 @@ private: else childCells.push_back(*it); } - ASSERT(IsSorted(parentCells.begin(), parentCells.end(), LessQueueOrder()), (parentCells)); - ASSERT(IsSorted(childCells.begin(), childCells.end(), LessQueueOrder()), (childCells)); + ASSERT(IsSorted(parentCells.begin(), parentCells.end(), LessLevelOrder()), (parentCells)); + ASSERT(IsSorted(childCells.begin(), childCells.end(), LessLevelOrder()), (childCells)); m_Covering[level].swap(childCells); parentCells.erase(unique(parentCells.begin(), parentCells.end()), parentCells.end()); AppendToVector(m_Covering[level - 1], parentCells); @@ -134,10 +134,10 @@ private: static void AppendToVector(vector & a, vector const & b) { - ASSERT(IsSortedAndUnique(a.begin(), a.end(), LessQueueOrder()), (a)); - ASSERT(IsSortedAndUnique(b.begin(), b.end(), LessQueueOrder()), (b)); + ASSERT(IsSortedAndUnique(a.begin(), a.end(), LessLevelOrder()), (a)); + ASSERT(IsSortedAndUnique(b.begin(), b.end(), LessLevelOrder()), (b)); vector merged; - set_union(a.begin(), a.end(), b.begin(), b.end(), back_inserter(merged), LessQueueOrder()); + set_union(a.begin(), a.end(), b.begin(), b.end(), back_inserter(merged), LessLevelOrder()); a.swap(merged); } @@ -164,7 +164,7 @@ private: void Sort() { for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - sort(m_Covering[level].begin(), m_Covering[level].end(), LessQueueOrder()); + sort(m_Covering[level].begin(), m_Covering[level].end(), LessLevelOrder()); } void Unique() @@ -172,7 +172,7 @@ private: for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) { vector & covering = m_Covering[level]; - ASSERT(IsSorted(covering.begin(), covering.end(), LessQueueOrder()), (covering)); + ASSERT(IsSorted(covering.begin(), covering.end(), LessLevelOrder()), (covering)); covering.erase(unique(covering.begin(), covering.end()), covering.end()); } } @@ -200,7 +200,7 @@ private: ++childLevel) { vector substracted; - CompareCellsAtLevel comparator(parentLevel); + CompareCellsAtLevel comparator(parentLevel); ASSERT(IsSorted(m_Covering[childLevel].begin(), m_Covering[childLevel].end(), comparator), (m_Covering[childLevel])); ASSERT(IsSorted(m_Covering[parentLevel].begin(), m_Covering[parentLevel].end(), comparator), diff --git a/geometry/geometry_tests/cellid_test.cpp b/geometry/geometry_tests/cellid_test.cpp index 6753121c90..3bc265e9f7 100644 --- a/geometry/geometry_tests/cellid_test.cpp +++ b/geometry/geometry_tests/cellid_test.cpp @@ -147,7 +147,7 @@ UNIT_TEST(CellID_LessQueueOrder) tst.push_back(m2::CellId<4>(e[i])); exp.push_back(m2::CellId<4>(v[i])); } - sort(tst.begin(), tst.end(), m2::CellId<4>::LessQueueOrder()); + sort(tst.begin(), tst.end(), m2::CellId<4>::LessLevelOrder()); TEST_EQUAL(tst, exp, ()); } while (next_permutation(e.begin(), e.end())); } @@ -171,7 +171,7 @@ UNIT_TEST(CellID_LessStackOrder) tst.push_back(m2::CellId<4>(e[i])); exp.push_back(m2::CellId<4>(v[i])); } - sort(tst.begin(), tst.end(), m2::CellId<4>::LessStackOrder()); + sort(tst.begin(), tst.end(), m2::CellId<4>::LessPreOrder()); TEST_EQUAL(tst, exp, ()); } while (next_permutation(e.begin(), e.end())); } @@ -180,7 +180,7 @@ UNIT_TEST(CellID_IsStringValid) { typedef m2::CellId<9> TId; TEST( TId::IsCellId("0123132"), () ); - TEST( !TId::IsCellId(""), () ); + TEST( TId::IsCellId(""), () ); TEST( !TId::IsCellId("-1332"), () ); TEST( !TId::IsCellId("023."), () ); TEST( !TId::IsCellId("121832"), () ); diff --git a/geometry/geometry_tests/covering_test.cpp b/geometry/geometry_tests/covering_test.cpp index a419b4354c..98fce5a855 100644 --- a/geometry/geometry_tests/covering_test.cpp +++ b/geometry/geometry_tests/covering_test.cpp @@ -84,7 +84,7 @@ UNIT_TEST(Covering_Append_Simple) c1.Append(covering::Covering(v2)); vector v4; c1.OutputToVector(v4); - sort(v4.begin(), v4.end(), CellId::LessStackOrder()); + sort(v4.begin(), v4.end(), CellId::LessPreOrder()); TEST_EQUAL(v3, v4, ()); }