[new downloader] Adding funtionality to SimpleTree and refactoring SimpleTree class. Renaming siblings to children. Fixing tests.

This commit is contained in:
Vladimir Byko-Ianko 2016-02-02 08:53:40 +03:00 committed by Sergey Yershov
parent 68b411a731
commit 2bbfe6a3ea
5 changed files with 152 additions and 88 deletions

View file

@ -130,7 +130,7 @@ namespace update
// using move semantics for mwmFiles
SizeUpdater sizeUpdater(dataDir, mwmFiles);
countries.ForEachChildren(sizeUpdater);
countries.ForEachDescendant(sizeUpdater);
}
storage::SaveCountries(my::TodayAsYYMMDD(), countries, jsonBuffer);

View file

@ -185,28 +185,28 @@ void LoadCountryCode2File(string const & jsonBuffer, multimap<string, string> &
template <class T>
void SaveImpl(T const & v, json_t * jParent)
{
size_t const siblingsCount = v.SiblingsCount();
CHECK_GREATER(siblingsCount, 0, ());
size_t const childrenCount = v.ChildrenCount();
CHECK_GREATER(childrenCount, 0, ());
my::JsonHandle jArray;
jArray.AttachNew(json_array());
for (size_t i = 0; i < siblingsCount; ++i)
for (size_t i = 0; i < childrenCount; ++i)
{
my::JsonHandle jCountry;
jCountry.AttachNew(json_object());
string const strName = v[i].Value().Name();
string const strName = v.Child(i).Value().Name();
CHECK(!strName.empty(), ("Empty country name?"));
json_object_set_new(jCountry.get(), "n", json_string(strName.c_str()));
string const strFlag = v[i].Value().Flag();
string const strFlag = v.Child(i).Value().Flag();
if (!strFlag.empty())
json_object_set_new(jCountry.get(), "c", json_string(strFlag.c_str()));
size_t countriesCount = v[i].Value().GetFilesCount();
size_t countriesCount = v.Child(i).Value().GetFilesCount();
ASSERT_LESS_OR_EQUAL(countriesCount, 1, ());
if (countriesCount > 0)
{
CountryFile const & file = v[i].Value().GetFile();
CountryFile const & file = v.Child(i).Value().GetFile();
string const & strFile = file.GetNameWithoutExt();
if (strFile != strName)
json_object_set_new(jCountry.get(), "f", json_string(strFile.c_str()));
@ -215,8 +215,8 @@ void SaveImpl(T const & v, json_t * jParent)
json_integer(file.GetRemoteSize(MapOptions::CarRouting)));
}
if (v[i].SiblingsCount())
SaveImpl(v[i], jCountry.get());
if (v.Child(i).ChildrenCount())
SaveImpl(v.Child(i), jCountry.get());
json_array_append(jArray.get(), jCountry.get());
}

View file

@ -1,15 +1,23 @@
#pragma once
#include "std/vector.hpp"
#include "base/assert.hpp"
#include "std/algorithm.hpp"
#include "std/vector.hpp"
template <class T>
class SimpleTree
{
typedef std::vector<SimpleTree<T> > internal_container_type;
T m_value;
internal_container_type m_siblings;
// @TODO(bykoianko) Remove on unnecessary methods of SimpleTree if any.
/// \brief m_children contains all first generation descendants of the node.
vector<SimpleTree<T>> m_children;
static bool IsEqual(T const & v1, T const & v2)
{
return !(v1 < v2) && !(v2 < v1);
}
public:
SimpleTree(T const & value = T()) : m_value(value)
@ -32,22 +40,22 @@ public:
T & AddAtDepth(int level, T const & value)
{
SimpleTree<T> * node = this;
while (level-- > 0 && !node->m_siblings.empty())
node = &node->m_siblings.back();
while (level-- > 0 && !node->m_children.empty())
node = &node->m_children.back();
return node->Add(value);
}
/// @return reference is valid only up to the next tree structure modification
T & Add(T const & value)
{
m_siblings.push_back(SimpleTree(value));
return m_siblings.back().Value();
m_children.emplace_back(SimpleTree(value));
return m_children.back().Value();
}
/// Deletes all children and makes tree empty
void Clear()
{
m_siblings.clear();
m_children.clear();
}
bool operator<(SimpleTree<T> const & other) const
@ -55,56 +63,112 @@ public:
return Value() < other.Value();
}
/// sorts siblings independently on each level by default
void Sort(bool onlySiblings = false)
/// sorts children independently on each level by default
void Sort()
{
std::sort(m_siblings.begin(), m_siblings.end());
if (!onlySiblings)
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
it->Sort(false);
sort(m_children.begin(), m_children.end());
for (auto & child : m_children)
child.Sort();
}
SimpleTree<T> const & operator[](size_t index) const
/// \brief Checks all nodes in tree to find an equal one. If there're several equal nodes
/// returns the first found.
/// \returns a poiter item in the tree if found and nullptr otherwise.
/// @TODO(bykoianko) The complexity of the method is O(n). But the structure (tree) is built on the start of the program
/// and then actively used on run time. This method (and class) should be redesigned to make the function work faster.
/// A hash table is being planned to use.
SimpleTree<T> const * const Find(T const & value) const
{
return m_siblings.at(index);
}
if (IsEqual(m_value, value))
return this;
size_t SiblingsCount() const
{
return m_siblings.size();
}
template <class TFunctor>
void ForEachSibling(TFunctor & f)
{
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
f(*it);
}
template <class TFunctor>
void ForEachSibling(TFunctor & f) const
{
for (typename internal_container_type::const_iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
f(*it);
}
template <class TFunctor>
void ForEachChildren(TFunctor & f)
{
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
for (auto const & child : m_children)
{
f(*it);
it->ForEachChildren(f);
SimpleTree<T> const * const found = child.Find(value);
if (found != nullptr)
return found;
}
return nullptr;
}
/// \brief Find only leaves.
/// \note It's a termprary fucntion for compatablity with old countries.txt.
/// When new countries.txt with unique ids will be added FindLeaf will be removed
/// and Find will be used intead.
/// @TODO(bykoianko) Remove this method on countries.txt update.
SimpleTree<T> const * const FindLeaf(T const & value) const
{
if (IsEqual(m_value, value) && m_children.empty())
return this; // It's a leaf.
for (auto const & child : m_children)
{
SimpleTree<T> const * const found = child.FindLeaf(value);
if (found != nullptr)
return found;
}
return nullptr;
}
SimpleTree<T> const & Child(size_t index) const
{
return m_children.at(index);
}
size_t ChildrenCount() const
{
return m_children.size();
}
/// \brief Calls functor f for each first generation descendant of the node.
template <class TFunctor>
void ForEachChild(TFunctor && f)
{
for (auto const & child : m_children)
f(child);
}
template <class TFunctor>
void ForEachChild(TFunctor && f) const
{
for (auto const & child : m_children)
f(child);
}
/// \brief Calls functor f for all nodes (add descendant) in the tree.
template <class TFunctor>
void ForEachDescendant(TFunctor && f)
{
for (auto & child : m_children)
{
f(child);
child.ForEachDescendant(f);
}
}
template <class TFunctor>
void ForEachChildren(TFunctor & f) const
void ForEachDescendant(TFunctor && f) const
{
for (typename internal_container_type::const_iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
for (auto const & child: m_children)
{
f(*it);
it->ForEachChildren(f);
f(child);
child.ForEachDescendant(f);
}
}
template <class TFunctor>
void ForEachInSubtree(TFunctor && f)
{
f(*this);
for (auto const & child: m_children)
child.ForEachInSubtree(f);
}
template <class TFunctor>
void ForEachInSubtree(TFunctor && f) const
{
f(*this);
for (auto const & child: m_children)
child.ForEachInSubtree(f);
}
};

View file

@ -205,19 +205,19 @@ size_t Storage::GetDownloadedFilesCount() const
CountriesContainerT const & NodeFromIndex(CountriesContainerT const & root, TIndex const & index)
{
// complex logic to avoid [] out_of_bounds exceptions
if (index.m_group == TIndex::INVALID || index.m_group >= static_cast<int>(root.SiblingsCount()))
if (index.m_group == TIndex::INVALID || index.m_group >= static_cast<int>(root.ChildrenCount()))
return root;
if (index.m_country == TIndex::INVALID ||
index.m_country >= static_cast<int>(root[index.m_group].SiblingsCount()))
index.m_country >= static_cast<int>(root.Child(index.m_group).ChildrenCount()))
{
return root[index.m_group];
return root.Child(index.m_group);
}
if (index.m_region == TIndex::INVALID ||
index.m_region >= static_cast<int>(root[index.m_group][index.m_country].SiblingsCount()))
index.m_region >= static_cast<int>(root.Child(index.m_group).Child(index.m_country).ChildrenCount()))
{
return root[index.m_group][index.m_country];
return root.Child(index.m_group).Child(index.m_country);
}
return root[index.m_group][index.m_country][index.m_region];
return root.Child(index.m_group).Child(index.m_country).Child(index.m_region);
}
Country const & Storage::CountryByIndex(TIndex const & index) const
@ -234,7 +234,7 @@ void Storage::GetGroupAndCountry(TIndex const & index, string & group, string &
size_t Storage::CountriesCount(TIndex const & index) const
{
return NodeFromIndex(m_countries, index).SiblingsCount();
return NodeFromIndex(m_countries, index).ChildrenCount();
}
string const & Storage::CountryName(TIndex const & index) const
@ -487,7 +487,7 @@ void Storage::LoadCountriesFile(bool forceReload)
if (forceReload)
m_countries.Clear();
if (m_countries.SiblingsCount() == 0)
if (m_countries.ChildrenCount() == 0)
{
string json;
string const name = migrate::NeedMigrate() ? COUNTRIES_FILE : COUNTRIES_MIGRATE_FILE;
@ -669,19 +669,19 @@ TIndex Storage::FindIndexByFile(string const & name) const
{
EqualFileName fn(name);
for (size_t i = 0; i < m_countries.SiblingsCount(); ++i)
for (size_t i = 0; i < m_countries.ChildrenCount(); ++i)
{
if (fn(m_countries[i]))
if (fn(m_countries.Child(i)))
return TIndex(static_cast<int>(i));
for (size_t j = 0; j < m_countries[i].SiblingsCount(); ++j)
for (size_t j = 0; j < m_countries.Child(i).ChildrenCount(); ++j)
{
if (fn(m_countries[i][j]))
if (fn(m_countries.Child(i).Child(j)))
return TIndex(static_cast<int>(i), static_cast<int>(j));
for (size_t k = 0; k < m_countries[i][j].SiblingsCount(); ++k)
for (size_t k = 0; k < m_countries.Child(i).Child(j).ChildrenCount(); ++k)
{
if (fn(m_countries[i][j][k]))
if (fn(m_countries.Child(i).Child(j).Child(k)))
return TIndex(static_cast<int>(i), static_cast<int>(j), static_cast<int>(k));
}
}
@ -695,19 +695,19 @@ vector<TIndex> Storage::FindAllIndexesByFile(string const & name) const
EqualFileName fn(name);
vector<TIndex> res;
for (size_t i = 0; i < m_countries.SiblingsCount(); ++i)
for (size_t i = 0; i < m_countries.ChildrenCount(); ++i)
{
if (fn(m_countries[i]))
if (fn(m_countries.Child(i)))
res.emplace_back(static_cast<int>(i));
for (size_t j = 0; j < m_countries[i].SiblingsCount(); ++j)
for (size_t j = 0; j < m_countries.Child(i).ChildrenCount(); ++j)
{
if (fn(m_countries[i][j]))
if (fn(m_countries.Child(i).Child(j)))
res.emplace_back(static_cast<int>(i), static_cast<int>(j));
for (size_t k = 0; k < m_countries[i][j].SiblingsCount(); ++k)
for (size_t k = 0; k < m_countries.Child(i).Child(j).ChildrenCount(); ++k)
{
if (fn(m_countries[i][j][k]))
if (fn(m_countries.Child(i).Child(j).Child(k)))
res.emplace_back(static_cast<int>(i), static_cast<int>(j), static_cast<int>(k));
}
}

View file

@ -35,25 +35,25 @@ UNIT_TEST(SimpleTree_Smoke)
tree.Sort();
// test sorting
TEST_EQUAL(tree[0].Value(), 1, ());
TEST_EQUAL(tree[1].Value(), 2, ());
TEST_EQUAL(tree[2].Value(), 3, ());
TEST_EQUAL(tree[3].Value(), 4, ());
TEST_EQUAL(tree[4].Value(), 5, ());
TEST_EQUAL(tree[0][0].Value(), 10, ());
TEST_EQUAL(tree[0][1].Value(), 20, ());
TEST_EQUAL(tree[0][2].Value(), 30, ());
TEST_EQUAL(tree.Child(0).Value(), 1, ());
TEST_EQUAL(tree.Child(1).Value(), 2, ());
TEST_EQUAL(tree.Child(2).Value(), 3, ());
TEST_EQUAL(tree.Child(3).Value(), 4, ());
TEST_EQUAL(tree.Child(4).Value(), 5, ());
TEST_EQUAL(tree.Child(0).Child(0).Value(), 10, ());
TEST_EQUAL(tree.Child(0).Child(1).Value(), 20, ());
TEST_EQUAL(tree.Child(0).Child(2).Value(), 30, ());
Calculator<TreeT> c1;
tree.ForEachSibling(c1);
tree.ForEachChild(c1);
TEST_EQUAL(c1.count, 5, ());
Calculator<TreeT> c2;
tree.ForEachChildren(c2);
tree.ForEachDescendant(c2);
TEST_EQUAL(c2.count, 8, ());
tree.Clear();
Calculator<TreeT> c3;
tree.ForEachChildren(c3);
tree.ForEachDescendant(c3);
TEST_EQUAL(c3.count, 0, ("Tree should be empty"));
}