From f9ad773dc479601093c1fa55736224879a6e45d9 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 4 Apr 2021 11:34:19 +0100 Subject: [PATCH] Added etl::histogram and etl::sparce_histogram --- include/etl/histogram.h | 606 ++++++++++++++++++++++++++++++++ test/test_maths_algorithms.cpp | 374 ++++++++++++++++++++ test/vs2019/etl.vcxproj | 2 + test/vs2019/etl.vcxproj.filters | 9 + 4 files changed, 991 insertions(+) create mode 100644 include/etl/histogram.h create mode 100644 test/test_maths_algorithms.cpp diff --git a/include/etl/histogram.h b/include/etl/histogram.h new file mode 100644 index 00000000..9d27288a --- /dev/null +++ b/include/etl/histogram.h @@ -0,0 +1,606 @@ +#pragma once + +#include "functional.h" +#include "algorithm.h" +#include "array.h" +#include "flat_map.h" +#include "static_assert.h" +#include "type_traits.h" +#include "integral_limits.h" + +namespace etl +{ + //*************************************************************************** + /// Histogram with a compile time start index. + //*************************************************************************** + template ::max> + class histogram + : public etl::unary_function + { + public: + + ETL_STATIC_ASSERT(etl::is_integral::value, "Only integral keys allowed"); + + static ETL_CONSTANT size_t Max_Size = Max_Keys; + + typedef TKey key_type; + typedef TCount count_type; + typedef TCount value_type; + typedef typename etl::array::const_iterator const_iterator; + + //********************************* + /// Constructor + //********************************* + histogram() + { + accumulator.fill(count_type(0)); + } + + //********************************* + /// Constructor + //********************************* + template + histogram(TIterator first, TIterator last) + { + accumulator.fill(count_type(0)); + add(first, last); + } + + //********************************* + /// Copy constructor + //********************************* + histogram(const histogram& other) + { + accumulator = other.accumulator; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move constructor + //********************************* + histogram(histogram&& other) + { + accumulator = etl::move(other.accumulator); + } +#endif + + //********************************* + /// Copy assignment + //********************************* + histogram& operator =(const histogram& rhs) + { + accumulator = rhs.accumulator; + + return *this; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move assignment + //********************************* + histogram& operator =(histogram&& rhs) + { + accumulator = etl::move(rhs.accumulator); + + return *this; + } +#endif + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator begin() const + { + return accumulator.begin(); + } + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator cbegin() const + { + return accumulator.cbegin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator end() const + { + return accumulator.begin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator cend() const + { + return accumulator.cbegin(); + } + + //********************************* + /// Add + //********************************* + void add(key_type key) + { + ++accumulator[key - Start_Index]; + } + + //********************************* + /// Add + //********************************* + template + void add(TIterator first, TIterator last) + { + while (first != last) + { + add(*first++); + } + } + + //********************************* + /// operator () + //********************************* + void operator ()(key_type key) + { + add(key); + } + + //********************************* + /// operator () + //********************************* + template + void operator ()(TIterator first, TIterator last) + { + add(first, last); + } + + //********************************* + /// operator [] + //********************************* + value_type operator [](key_type key) const + { + return accumulator[key]; + } + + //********************************* + /// Clear the histogram. + //********************************* + void clear() + { + accumulator.fill(count_type(0)); + } + + //********************************* + /// Size of the histogram. + //********************************* + ETL_CONSTEXPR size_t size() const + { + return Max_Keys; + } + + //********************************* + /// Max size of the histogram. + //********************************* + ETL_CONSTEXPR size_t max_size() const + { + return Max_Keys; + } + + //********************************* + /// Count of items in the histogram. + //********************************* + size_t count() const + { + return etl::accumulate(accumulator.begin(), accumulator.end(), size_t(0)); + } + + private: + + etl::array accumulator; + }; + + //*************************************************************************** + /// Histogram with a run time start index. + //*************************************************************************** + template + class histogram::max> + : public etl::unary_function + { + public: + + ETL_STATIC_ASSERT(etl::is_integral::value, "Only integral keys allowed"); + + static ETL_CONSTANT size_t Max_Size = Max_Keys; + + typedef TKey key_type; + typedef TCount count_type; + typedef TCount value_type; + typedef typename etl::array::const_iterator const_iterator; + + //********************************* + /// Constructor + //********************************* + explicit histogram(key_type start_index_) + : start_index(start_index_) + { + accumulator.fill(count_type(0)); + } + + //********************************* + /// Constructor + //********************************* + template + histogram(key_type start_index_, TIterator first, TIterator last) + : start_index(start_index_) + { + accumulator.fill(count_type(0)); + add(first, last); + } + + //********************************* + /// Copy constructor + //********************************* + histogram(const histogram& other) + { + accumulator = other.accumulator; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move constructor + //********************************* + histogram(histogram&& other) + { + accumulator = etl::move(other.accumulator); + } +#endif + + //********************************* + /// Copy assignment + //********************************* + histogram& operator =(const histogram& rhs) + { + accumulator = rhs.accumulator; + + return *this; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move assignment + //********************************* + histogram& operator =(histogram&& rhs) + { + accumulator = etl::move(rhs.accumulator); + + return *this; + } +#endif + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator begin() const + { + return accumulator.begin(); + } + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator cbegin() const + { + return accumulator.cbegin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator end() const + { + return accumulator.begin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator cend() const + { + return accumulator.cbegin(); + } + + //********************************* + /// Add + //********************************* + void add(key_type key) + { + ++accumulator[key - start_index]; + } + + //********************************* + /// Add + //********************************* + template + void add(TIterator first, TIterator last) + { + while (first != last) + { + add(*first++); + } + } + + //********************************* + /// operator () + //********************************* + void operator ()(key_type key) + { + add(key); + } + + //********************************* + /// operator () + //********************************* + template + void operator ()(TIterator first, TIterator last) + { + add(first, last); + } + + //********************************* + /// operator [] + //********************************* + value_type operator [](key_type key) const + { + return accumulator[key]; + } + + //********************************* + /// Clear the histogram. + //********************************* + void clear() + { + accumulator.fill(count_type(0)); + } + + //********************************* + /// Size of the histogram. + //********************************* + ETL_CONSTEXPR size_t size() const + { + return Max_Keys; + } + + //********************************* + /// Max size of the histogram. + //********************************* + ETL_CONSTEXPR size_t max_size() const + { + return Max_Keys; + } + + //********************************* + /// Count of items in the histogram. + //********************************* + size_t count() const + { + return etl::accumulate(accumulator.begin(), accumulator.end(), size_t(0)); + } + + private: + + key_type start_index; + etl::array accumulator; + }; + + //*************************************************************************** + /// Histogram for sparce keys. + //*************************************************************************** + template + class sparce_histogram : public etl::unary_function + { + private: + + typedef etl::flat_map accumulator_type; + + public: + + static ETL_CONSTANT size_t Max_Size = Max_Keys; + + typedef TKey key_type; + typedef TCount count_type; + typedef typename accumulator_type::value_type value_type; + typedef typename accumulator_type::const_iterator const_iterator; + + public: + + //********************************* + /// Constructor + //********************************* + sparce_histogram() + { + } + + //********************************* + /// Constructor + //********************************* + template + sparce_histogram(TIterator first, TIterator last) + { + add(first, last); + } + + //********************************* + /// Copy constructor + //********************************* + sparce_histogram(const sparce_histogram& other) + { + accumulator = other.accumulator; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move constructor + //********************************* + sparce_histogram(sparce_histogram&& other) + { + accumulator = etl::move(other.accumulator); + } +#endif + + //********************************* + /// Copy assignment + //********************************* + sparce_histogram& operator =(const sparce_histogram& rhs) + { + accumulator = rhs.accumulator; + + return *this; + } + +#if ETL_CPP11_SUPPORTED + //********************************* + /// Move assignment + //********************************* + sparce_histogram& operator =(sparce_histogram&& rhs) + { + accumulator = etl::move(rhs.accumulator); + + return *this; + } +#endif + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator begin() const + { + return accumulator.begin(); + } + + //********************************* + /// Beginning of the histogram. + //********************************* + const_iterator cbegin() const + { + return accumulator.cbegin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator end() const + { + return accumulator.begin(); + } + + //********************************* + /// End of the histogram. + //********************************* + const_iterator cend() const + { + return accumulator.cbegin(); + } + + //********************************* + /// Add + //********************************* + void add(const key_type& key) + { + ++accumulator[key]; + } + + //********************************* + /// Add + //********************************* + template + void add(TIterator first, TIterator last) + { + while (first != last) + { + add(*first++); + } + } + + //********************************* + /// operator () + //********************************* + void operator ()(const key_type& key) + { + add(key); + } + + //********************************* + /// operator () + //********************************* + template + void operator ()(TIterator first, TIterator last) + { + add(first, last); + } + + //********************************* + /// operator [] + //********************************* + const value_type& operator [](const key_type& key) const + { + static const value_type unused(key_type(), count_type(0)); + + accumulator_type::const_iterator itr = accumulator.find(key); + + if (itr != accumulator.end()) + { + return *itr; + } + else + { + return unused; + } + } + + //********************************* + /// Clear the histogram. + //********************************* + void clear() + { + accumulator.clear(); + } + + //********************************* + /// Size of the histogram. + //********************************* + size_t size() const + { + return accumulator.size(); + } + + //********************************* + /// Max size of the histogram. + //********************************* + ETL_CONSTEXPR size_t max_size() const + { + return Max_Keys; + } + + //********************************* + /// Count of items in the histogram. + //********************************* + size_t count() const + { + count_type sum = count_type(0); + + const_iterator itr = accumulator.begin(); + + while (itr != accumulator.end()) + { + sum += (*itr++).second; + } + + return sum; + } + + private: + + etl::flat_map accumulator; + }; +} diff --git a/test/test_maths_algorithms.cpp b/test/test_maths_algorithms.cpp new file mode 100644 index 00000000..d4c84fdb --- /dev/null +++ b/test/test_maths_algorithms.cpp @@ -0,0 +1,374 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2021 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/algorithm.h" +#include "etl/histogram.h" + +#include +#include +#include +#include + +namespace +{ + constexpr size_t Size = 10U; + constexpr int Start = -4; + + + using IntRuntimeOffsetHistogram = etl::histogram; + using IntOffset0Histogram = etl::histogram; + using IntOffsetminus4Histogram = etl::histogram; + using StringHistogram = etl::sparce_histogram; + + std::array input1 = + { + 5, 5, 5, 5, 5, 5, + 4, 4, 4, 4, 4, + 6, 6, 6, 6, 6, 6, 6, + 3, 3, 3, 3, + 7, 7, 7, 7, 7, 7, 7, 7, + 2, 2, 2, + 8, 8, 8, 8, 8, 8, 8, 8, 8, + 1, 1, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0 + }; + + std::array input2 = + { + 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, + -1, -1, -1, -1, + 3, 3, 3, 3, 3, 3, 3, 3, + -2, -2, -2, + 4, 4, 4, 4, 4, 4, 4, 4, 4, + -3, -3, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + -4 + }; + + std::array input3 = + { + "5", "5", "5", "5", "5", "5", + "4", "4", "4", "4", "4", + "6", "6", "6", "6", "6", "6", "6", + "3", "3", "3", "3", + "7", "7", "7", "7", "7", "7", "7", "7", + "2", "2", "2", + "8", "8", "8", "8", "8", "8", "8", "8", "8", + "1", "1", + "9", "9", "9", "9", "9", "9", "9", "9", "9", "9", + "0" + }; + + std::array output1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + std::array, Size> output2 = + { + ETL_OR_STD::pair("0", 1), + ETL_OR_STD::pair("1", 2), + ETL_OR_STD::pair("2", 3), + ETL_OR_STD::pair("3", 4), + ETL_OR_STD::pair("4", 5), + ETL_OR_STD::pair("5", 6), + ETL_OR_STD::pair("6", 7), + ETL_OR_STD::pair("7", 8), + ETL_OR_STD::pair("8", 9), + ETL_OR_STD::pair("9", 10), + }; + + std::array zero1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + std::array, Size> zero2 = + { + ETL_OR_STD::pair("0", 0), + ETL_OR_STD::pair("1", 0), + ETL_OR_STD::pair("2", 0), + ETL_OR_STD::pair("3", 0), + ETL_OR_STD::pair("4", 0), + ETL_OR_STD::pair("5", 0), + ETL_OR_STD::pair("6", 0), + ETL_OR_STD::pair("7", 0), + ETL_OR_STD::pair("8", 0), + ETL_OR_STD::pair("9", 0), + }; + + SUITE(test_maths_algorithms) + { + //************************************************************************* + TEST(test_int_offset_0_histogram_constructor) + { + IntOffset0Histogram histogram; + + CHECK_EQUAL(0U, histogram.count()); + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(test_int_offset_0_histogram) + { + IntOffset0Histogram histogram(input1.begin(), input1.end()); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram.begin()); + CHECK(isEqual); + + CHECK_EQUAL(55U, histogram.count()); + + histogram.clear(); + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(isEqual); + CHECK_EQUAL(0U, histogram.count()); + } + + //************************************************************************* + TEST(test_int_functor1_offset_0_histogram) + { + IntOffset0Histogram histogram; + + histogram = std::for_each(input1.begin(), input1.end(), histogram); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(test_int_functor2_offset_0_histogram) + { + IntOffset0Histogram histogram; + + histogram(input1.begin(), input1.end()); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(test_int_offset_0_histogram_indexed) + { + IntOffset0Histogram histogram(input1.begin(), input1.end()); + + CHECK_EQUAL(Size, histogram.size()); + + for (size_t i = 0; i < output1.size(); ++i) + { + CHECK_EQUAL(int(output1[i]), int(histogram[i])); + } + } + + //************************************************************************* + TEST(test_int_offset_minus_4_histogram) + { + IntOffsetminus4Histogram histogram(input2.begin(), input2.end()); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram.begin()); + CHECK(isEqual); + + CHECK_EQUAL(55U, histogram.count()); + + histogram.clear(); + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram.begin()); + CHECK(isEqual); + CHECK_EQUAL(0U, histogram.count()); + } + + //************************************************************************* + TEST(test_int_offset_minus_4_histogram_indexed) + { + IntOffsetminus4Histogram histogram(input2.begin(), input2.end()); + + CHECK_EQUAL(Size, histogram.size()); + + for (size_t i = 0; i < output1.size(); ++i) + { + CHECK_EQUAL(int(output1[i]), int(histogram[i])); + } + } + + //************************************************************************* + TEST(test_int_runtime_offset_minus_4_histogram) + { + IntRuntimeOffsetHistogram histogram1(Start); + IntRuntimeOffsetHistogram histogram2(Start, input2.begin(), input2.end()); + + histogram1.add(input2.begin(), input2.end()); + + CHECK_EQUAL(Size, histogram1.size()); + CHECK_EQUAL(Size, histogram2.size()); + + bool isEqual; + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram1.begin()); + CHECK(!isEqual); + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram2.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram1.begin()); + CHECK(isEqual); + + isEqual = std::equal(output1.begin(), output1.end(), histogram2.begin()); + CHECK(isEqual); + + CHECK_EQUAL(55U, histogram1.count()); + CHECK_EQUAL(55U, histogram2.count()); + + histogram1.clear(); + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram1.begin()); + CHECK(isEqual); + CHECK_EQUAL(0U, histogram1.count()); + + histogram2.clear(); + + isEqual = std::equal(zero1.begin(), zero1.end(), histogram2.begin()); + CHECK(isEqual); + CHECK_EQUAL(0U, histogram2.count()); + } + + //************************************************************************* + TEST(test_string_histogram_constructor) + { + StringHistogram histogram; + + CHECK_EQUAL(0U, histogram.size()); + CHECK_EQUAL(0U, histogram.count()); + } + + //************************************************************************* + TEST(test_string_histogram) + { + StringHistogram histogram(input3.begin(), input3.end()); + + CHECK_EQUAL(Size, histogram.size()); + CHECK_EQUAL(55U, histogram.count()); + + bool isEqual; + + isEqual = std::equal(zero2.begin(), zero2.end(), histogram.begin()); + CHECK(!isEqual); + + auto o1 = *output2.begin(); + auto h1 = *histogram.begin(); + + isEqual = std::equal(output2.begin(), output2.end(), histogram.begin()); + CHECK(isEqual); + + CHECK_EQUAL(55U, histogram.count()); + + histogram.clear(); + + CHECK(isEqual); + CHECK_EQUAL(0U, histogram.size()); + CHECK_EQUAL(0U, histogram.count()); + } + + //************************************************************************* + TEST(test_string_histogram_unused_keys) + { + StringHistogram histogram(input3.begin(), input3.end()); + + CHECK(histogram["5"] == output2[5]); + + StringHistogram::value_type unused = histogram[std::string("99")]; + CHECK(unused.first == std::string()); + CHECK(unused.second == 0U); + } + + //************************************************************************* + TEST(test_string_functor1_histogram) + { + StringHistogram histogram; + + histogram = std::for_each(input3.begin(), input3.end(), histogram); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero2.begin(), zero2.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output2.begin(), output2.end(), histogram.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(test_string_functor2_histogram) + { + StringHistogram histogram; + + histogram(input3.begin(), input3.end()); + + CHECK_EQUAL(Size, histogram.size()); + + bool isEqual; + + isEqual = std::equal(zero2.begin(), zero2.end(), histogram.begin()); + CHECK(!isEqual); + + isEqual = std::equal(output2.begin(), output2.end(), histogram.begin()); + CHECK(isEqual); + } + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 3b2c46f2..d69ec9f0 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -1323,6 +1323,7 @@ + @@ -4196,6 +4197,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index 3cbca3d5..fbcc11bf 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -106,6 +106,9 @@ {562466b5-677d-4448-9e9e-f70805cd71ad} + + {e2ef7213-8d01-4490-8735-2001456cbd88} + @@ -1059,6 +1062,9 @@ ETL\Maths + + ETL\Functors + @@ -2393,6 +2399,9 @@ Source Files + + Source Files +