/******************************************************************************
The MIT License(MIT)

Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com

Copyright(c) 2014 John Wellbelove

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/binary.h"
#include "etl/container.h"

#include "data.h"
#include "iterators_for_unit_tests.h"

#include <algorithm>
#include <array>
#include <forward_list>
#include <functional>
#include <list>
#include <memory>
#include <numeric>
#include <random>
#include <vector>

namespace
{
  using NDC   = TestDataNDC<int>;
  using ItemM = TestDataM<std::string>;
  std::random_device rng;
  std::mt19937       urng(rng());

  using Vector = std::vector<int>;
  Vector data  = {2, 1, 1, 4, 3, 6, 5, 8, 7, 10, 10, 9};

  using VectorM = std::vector<ItemM>;

  constexpr size_t SIZE = 10;

  int dataA[SIZE] = {2, 1, 4, 3, 6, 5, 8, 7, 10, 9};

  int            dataS[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  std::list<int> dataSL      = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  using List = std::list<int>;
  List dataL = {2, 1, 4, 3, 6, 5, 8, 7, 10, 9};

  int            dataEQ[SIZE] = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9};
  std::list<int> dataEQL      = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9};

  Vector dataV = {2, 1, 4, 3, 6, 5, 8, 7, 10, 9};

  int dataD1[SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  int dataD2[SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

  class Data
  {
  public:

    Data()
      : a(0)
      , b(0)
    {
    }

    Data(int a_, int b_)
      : a(a_)
      , b(b_)
    {
    }

    int a;
    int b;
  };

  bool operator==(const Data& lhs, const Data& rhs)
  {
    return (lhs.a == rhs.a) && (lhs.b == rhs.b);
  }

  struct DataPredicate
  {
    bool operator()(const Data& lhs, const Data& rhs) const
    {
      return lhs.a < rhs.a;
    }
  };

  struct DataEquality
  {
    bool operator()(const Data& lhs, const Data& rhs) const
    {
      return lhs.a == rhs.a;
    }
  };

  struct DataEquivalenceByA : public etl::binary_function<Data, Data, bool>
  {
    bool operator()(const Data& lhs, const Data& rhs) const
    {
      return lhs.a == rhs.a;
    }
  };

  Data dataD[10] = {Data(1, 2), Data(2, 1), Data(3, 4), Data(4, 3), Data(5, 6), Data(6, 5), Data(7, 8), Data(8, 7), Data(9, 10), Data(10, 9)};

  struct Greater : public etl::binary_function<int, int, bool>
  {
    bool operator()(int a, int b) const
    {
      return a > b;
    }
  };

  std::ostream& operator<<(std::ostream& os, const Data& data_)
  {
    os << data_.a << "," << data_.b;
    return os;
  }

  SUITE(test_algorithm)
  {
    //*************************************************************************
    TEST(min)
    {
      int a = 1;
      int b = 2;

      CHECK_EQUAL((std::min(a, b)), (etl::min(a, b)));
      CHECK_EQUAL((std::min(b, a)), (etl::min(b, a)));
    }

    //*************************************************************************
    TEST(min_compare)
    {
      int a = 1;
      int b = 2;

      CHECK_EQUAL((std::min(a, b, Greater())), (etl::min(a, b, Greater())));
      CHECK_EQUAL((std::min(b, a, Greater())), (etl::min(b, a, Greater())));
    }

    //*************************************************************************
    TEST(max)
    {
      int a = 1;
      int b = 2;

      CHECK_EQUAL((std::max(a, b)), (etl::max(a, b)));
      CHECK_EQUAL((std::max(b, a)), (etl::max(b, a)));
    }

    //*************************************************************************
    TEST(max_compare)
    {
      int a = 1;
      int b = 2;

      CHECK_EQUAL((std::max(a, b, Greater())), (etl::max(a, b, Greater())));
      CHECK_EQUAL((std::max(b, a, Greater())), (etl::max(b, a, Greater())));
    }

    //*************************************************************************
    TEST(min_element)
    {
      Vector::iterator expected = std::min_element(data.begin(), data.end());
      Vector::iterator result   = etl::min_element(data.begin(), data.end());
      CHECK_EQUAL(std::distance(data.begin(), expected), std::distance(data.begin(), result));
    }

    //*************************************************************************
    TEST(min_element_compare)
    {
      Vector::iterator expected = std::min_element(data.begin(), data.end(), std::greater<int>());
      Vector::iterator result   = etl::min_element(data.begin(), data.end(), std::greater<int>());
      CHECK_EQUAL(std::distance(data.begin(), expected), std::distance(data.begin(), result));
    }

    //*************************************************************************
    TEST(min_element_empty)
    {
      Vector           dataEmpty;
      Vector::iterator expected = std::min_element(dataEmpty.begin(), dataEmpty.end());
      Vector::iterator result   = etl::min_element(dataEmpty.begin(), dataEmpty.end());
      CHECK_EQUAL(std::distance(dataEmpty.end(), expected), std::distance(dataEmpty.end(), result));
    }

    //*************************************************************************
    TEST(max_element)
    {
      Vector::iterator expected = std::max_element(data.begin(), data.end());
      Vector::iterator result   = etl::max_element(data.begin(), data.end());
      CHECK_EQUAL(std::distance(data.begin(), expected), std::distance(data.begin(), result));
    }

    //*************************************************************************
    TEST(max_element_compare)
    {
      Vector::iterator expected = std::max_element(data.begin(), data.end(), std::greater<int>());
      Vector::iterator result   = etl::max_element(data.begin(), data.end(), std::greater<int>());
      CHECK_EQUAL(std::distance(data.begin(), expected), std::distance(data.begin(), result));
    }

    //*************************************************************************
    TEST(max_element_empty)
    {
      Vector           dataEmpty;
      Vector::iterator expected = std::max_element(dataEmpty.begin(), dataEmpty.end());
      Vector::iterator result   = etl::max_element(dataEmpty.begin(), dataEmpty.end());
      CHECK_EQUAL(std::distance(dataEmpty.end(), expected), std::distance(dataEmpty.end(), result));
    }

    //*************************************************************************
    TEST(minmax_element)
    {
      std::pair<Vector::iterator, Vector::iterator> expected = std::minmax_element(data.begin(), data.end());
      std::pair<Vector::iterator, Vector::iterator> result   = etl::minmax_element(data.begin(), data.end());
      CHECK_EQUAL(std::distance(data.begin(), expected.first), std::distance(data.begin(), result.first));
      CHECK_EQUAL(std::distance(data.begin(), expected.second), std::distance(data.begin(), result.second));
    }

    //*************************************************************************
    TEST(minmax_element_compare)
    {
      std::pair<Vector::iterator, Vector::iterator> expected = std::minmax_element(data.begin(), data.end(), std::greater<int>());
      std::pair<Vector::iterator, Vector::iterator> result   = etl::minmax_element(data.begin(), data.end(), std::greater<int>());
      CHECK_EQUAL(std::distance(data.begin(), expected.first), std::distance(data.begin(), result.first));
      CHECK_EQUAL(std::distance(data.begin(), expected.second), std::distance(data.begin(), result.second));
    }

    //*************************************************************************
    TEST(minmax_element_empty)
    {
      Vector                                        dataEmpty;
      std::pair<Vector::iterator, Vector::iterator> expected = std::minmax_element(dataEmpty.begin(), dataEmpty.end());
      std::pair<Vector::iterator, Vector::iterator> result   = etl::minmax_element(dataEmpty.begin(), dataEmpty.end());
      CHECK_EQUAL(std::distance(dataEmpty.begin(), expected.first), std::distance(dataEmpty.begin(), result.first));
      CHECK_EQUAL(std::distance(dataEmpty.begin(), expected.second), std::distance(dataEmpty.begin(), result.second));
    }

    //*************************************************************************
    TEST(minmax)
    {
      int a = 1;
      int b = 2;

      std::pair<int, int> expected = std::minmax(a, b);
      std::pair<int, int> result   = etl::minmax(a, b);
      CHECK_EQUAL(expected.first, result.first);
      CHECK_EQUAL(expected.second, result.second);

      result   = etl::minmax(b, a);
      expected = std::minmax(b, a);
      CHECK_EQUAL(expected.first, result.first);
      CHECK_EQUAL(expected.second, result.second);
    }

    //*************************************************************************
    TEST(minmax_compare)
    {
      int a = 1;
      int b = 2;

      std::pair<int, int> expected = std::minmax(a, b, std::greater<int>());
      std::pair<int, int> result   = etl::minmax(a, b, std::greater<int>());
      CHECK_EQUAL(expected.first, result.first);
      CHECK_EQUAL(expected.second, result.second);

      result   = etl::minmax(b, a, std::greater<int>());
      expected = std::minmax(b, a, std::greater<int>());
      CHECK_EQUAL(expected.first, result.first);
      CHECK_EQUAL(expected.second, result.second);
    }

    //*************************************************************************
    TEST(is_sorted_until)
    {
      int data[] = {1, 2, 3, 4, 6, 5, 7, 8, 9, 10};

      int* p1 = std::is_sorted_until(std::begin(data), std::end(data));
      int* p2 = etl::is_sorted_until(std::begin(data), std::end(data));
      CHECK_EQUAL(std::distance(std::begin(data), p1), std::distance(std::begin(data), p2));
    }

    //*************************************************************************
    TEST(is_sorted_until_compare)
    {
      int data[] = {10, 9, 8, 7, 5, 6, 4, 3, 4, 2, 1};

      int* p1 = std::is_sorted_until(std::begin(data), std::end(data), std::greater<int>());
      int* p2 = etl::is_sorted_until(std::begin(data), std::end(data), std::greater<int>());
      CHECK_EQUAL(std::distance(std::begin(data), p1), std::distance(std::begin(data), p2));
    }

    //*************************************************************************
    TEST(is_sorted)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      bool is_sorted = etl::is_sorted(std::begin(data1), std::end(data1));
      CHECK(is_sorted);

      int data2[] = {1, 2, 3, 4, 6, 5, 7, 8, 9, 10};

      is_sorted = etl::is_sorted(std::begin(data2), std::end(data2));
      CHECK(!is_sorted);
    }

    //*************************************************************************
    TEST(is_sorted_compare)
    {
      int data1[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};

      bool is_sorted = etl::is_sorted(std::begin(data1), std::end(data1), std::greater<int>());
      CHECK(is_sorted);

      int data2[] = {10, 9, 8, 7, 5, 6, 4, 3, 2, 1};

      is_sorted = etl::is_sorted(std::begin(data2), std::end(data2), std::greater<int>());
      CHECK(!is_sorted);
    }

    //*************************************************************************
    TEST(is_unique_sorted_until)
    {
      int sorted_data[]     = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int not_sorted_data[] = {1, 2, 3, 4, 6, 5, 7, 8, 9, 10};
      int not_unique_data[] = {1, 2, 3, 4, 4, 5, 6, 8, 9, 10};

      int* p_sorted     = etl::is_unique_sorted_until(std::begin(sorted_data), std::end(sorted_data));
      int* p_not_sorted = etl::is_unique_sorted_until(std::begin(not_sorted_data), std::end(not_sorted_data));
      int* p_not_unique = etl::is_unique_sorted_until(std::begin(not_unique_data), std::end(not_unique_data));

      CHECK_EQUAL(10, std::distance(sorted_data, p_sorted));
      CHECK_EQUAL(5, std::distance(not_sorted_data, p_not_sorted));
      CHECK_EQUAL(4, std::distance(not_unique_data, p_not_unique));
    }

    //*************************************************************************
    TEST(is_unique_sorted_until_compare)
    {
      int sorted_data[]     = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
      int not_sorted_data[] = {10, 9, 8, 7, 5, 6, 4, 3, 2, 1};
      int not_unique_data[] = {10, 9, 8, 6, 5, 4, 4, 3, 2, 1};

      int* p_sorted     = etl::is_unique_sorted_until(std::begin(sorted_data), std::end(sorted_data), std::greater<int>());
      int* p_not_sorted = etl::is_unique_sorted_until(std::begin(not_sorted_data), std::end(not_sorted_data), std::greater<int>());
      int* p_not_unique = etl::is_unique_sorted_until(std::begin(not_unique_data), std::end(not_unique_data), std::greater<int>());

      CHECK_EQUAL(10, std::distance(sorted_data, p_sorted));
      CHECK_EQUAL(5, std::distance(not_sorted_data, p_not_sorted));
      CHECK_EQUAL(6, std::distance(not_unique_data, p_not_unique));
    }

    //*************************************************************************
    TEST(is_unique_sorted)
    {
      int sorted_data[]     = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int not_sorted_data[] = {1, 2, 3, 4, 6, 5, 7, 8, 9, 10};
      int not_unique_data[] = {1, 2, 3, 4, 4, 5, 6, 8, 9, 10};

      CHECK_TRUE((etl::is_unique_sorted(std::begin(sorted_data), std::end(sorted_data))));
      CHECK_FALSE((etl::is_unique_sorted(std::begin(not_sorted_data), std::end(not_sorted_data))));
      CHECK_FALSE((etl::is_unique_sorted(std::begin(not_unique_data), std::end(not_unique_data))));
    }

    //*************************************************************************
    TEST(is_unique_sorted_compare)
    {
      int sorted_data[]     = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
      int not_sorted_data[] = {10, 9, 8, 7, 5, 6, 4, 3, 2, 1};
      int not_unique_data[] = {10, 9, 8, 6, 5, 4, 4, 3, 2, 1};

      CHECK_TRUE((etl::is_unique_sorted(std::begin(sorted_data), std::end(sorted_data), std::greater<int>())));
      CHECK_FALSE((etl::is_unique_sorted(std::begin(not_sorted_data), std::end(not_sorted_data), std::greater<int>())));
      CHECK_FALSE((etl::is_unique_sorted(std::begin(not_unique_data), std::end(not_unique_data), std::greater<int>())));
    }

    //*************************************************************************
    TEST(copy_pod_pointer)
    {
      int data1[10];
      int data2[10];

      int* pstl = std::copy(std::begin(dataA), std::end(dataA), std::begin(data1));
      int* petl = etl::copy(std::begin(dataA), std::end(dataA), std::begin(data2));

      using difference_type_t = std::iterator_traits<int*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_non_pod_pointer)
    {
      Data data1[10];
      Data data2[10];

      Data* pstl = std::copy(std::begin(dataD), std::end(dataD), std::begin(data1));
      Data* petl = etl::copy(std::begin(dataD), std::end(dataD), std::begin(data2));

      using difference_type_t = std::iterator_traits<Data*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_non_random_iterator)
    {
      List data1(dataL.size());
      List data2(dataL.size());

      List::iterator pstl = std::copy(std::begin(dataA), std::end(dataA), std::begin(data1));
      List::iterator petl = etl::copy(std::begin(dataA), std::end(dataA), std::begin(data2));

      using difference_type_t = List::difference_type;

      difference_type_t dstl = std::distance(data1.begin(), pstl);
      difference_type_t detl = std::distance(data2.begin(), petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_n_random_iterator)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int data2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int data3[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      int* result;

      std::copy_n(std::begin(data1), 4, std::begin(data2));
      result = etl::copy_n(std::begin(data1), 4, std::begin(data3));

      CHECK_EQUAL(std::begin(data3) + 4, result);

      bool is_same = std::equal(std::begin(data2), std::end(data2), std::begin(data3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_n_non_random_iterator)
    {
      std::list<int> data1   = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int            data2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int            data3[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      int* result;

      std::copy_n(std::begin(data1), 4, std::begin(data2));
      result = etl::copy_n(std::begin(data1), 4, std::begin(data3));

      CHECK_EQUAL(std::begin(data3) + 4, result);

      bool is_same = std::equal(std::begin(data2), std::end(data2), std::begin(data3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_if)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int data2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int data3[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      // Copy everything less than 5.
      std::copy_if(std::begin(data1), std::end(data1), std::begin(data2), std::bind(std::less<int>(), std::placeholders::_1, 5));
      etl::copy_if(std::begin(data1), std::end(data1), std::begin(data3), std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool is_same = std::equal(std::begin(data2), std::end(data2), std::begin(data3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(reverse_copy_pod_pointer)
    {
      int data1[10];
      int data2[10];

      int* pstl = std::reverse_copy(std::begin(dataA), std::end(dataA), std::begin(data1));
      int* petl = etl::reverse_copy(std::begin(dataA), std::end(dataA), std::begin(data2));

      using difference_type_t = std::iterator_traits<int*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_n_pod_pointer)
    {
      int data1[10];
      int data2[10];

      int* pstl = std::copy_n(std::begin(dataA), 10, std::begin(data1));
      int* petl = etl::copy_n(std::begin(dataA), 10, std::begin(data2));

      using difference_type_t = std::iterator_traits<int*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_n_non_pod_pointer)
    {
      Data data1[10];
      Data data2[10];

      Data* pstl = std::copy_n(std::begin(dataD), 10, std::begin(data1));
      Data* petl = etl::copy_n(std::begin(dataD), 10, std::begin(data2));

      using difference_type_t = std::iterator_traits<Data*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_backward_pod_pointer)
    {
      int data1[10];
      int data2[10];

      int* pstl = std::copy_backward(std::begin(dataA), std::end(dataA), std::end(data1));
      int* petl = etl::copy_backward(std::begin(dataA), std::end(dataA), std::end(data2));

      using difference_type_t = std::iterator_traits<int*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_backward_non_pod_pointer)
    {
      Data data1[10];
      Data data2[10];

      Data* pstl = std::copy_backward(std::begin(dataD), std::end(dataD), std::end(data1));
      Data* petl = etl::copy_backward(std::begin(dataD), std::end(dataD), std::end(data2));

      using difference_type_t = std::iterator_traits<Data*>::difference_type;

      difference_type_t dstl = std::distance(data1, pstl);
      difference_type_t detl = std::distance(data2, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(copy_backward_non_random_iterator)
    {
      List data1(dataL.size());
      List data2(dataL.size());

      List::iterator pstl = copy_backward(std::begin(dataA), std::end(dataA), std::end(data1));
      List::iterator petl = etl::copy_backward(std::begin(dataA), std::end(dataA), std::end(data2));

      using difference_type_t = std::iterator_traits<List::iterator>::difference_type;

      difference_type_t dstl = std::distance(data1.begin(), pstl);
      difference_type_t detl = std::distance(data2.begin(), petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(test_reverse_even_non_pointer)
    {
      std::array<int, 10> data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
      std::array<int, 10> data2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

      std::reverse(data1.begin(), data1.end());
      etl::reverse(data2.begin(), data2.end());

      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(test_reverse_odd_non_pointer)
    {
      std::array<int, 9> data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      std::array<int, 9> data2 = {0, 1, 2, 3, 4, 5, 6, 7, 8};

      std::reverse(data1.begin(), data1.end());
      etl::reverse(data2.begin(), data2.end());

      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(test_reverse_even_pointer)
    {
      int data1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
      int data2[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

      std::reverse(std::begin(data1), std::end(data1));
      etl::reverse(std::begin(data2), std::end(data2));

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(test_reverse_odd_pointer)
    {
      int data1[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      int data2[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

      std::reverse(std::begin(data1), std::end(data1));
      etl::reverse(std::begin(data2), std::end(data2));

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(lower_bound_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        int* lb1 = std::lower_bound(std::begin(dataS), std::end(dataS), i);
        int* lb2 = etl::lower_bound(random_iterator<int>(std::begin(dataS)), random_iterator<int>(std::end(dataS)), i);

        CHECK_EQUAL(lb1, lb2);
      }
    }

    //*************************************************************************
    TEST(lower_bound_non_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        int* lb1 = std::lower_bound(std::begin(dataS), std::end(dataS), i);
        int* lb2 = etl::lower_bound(non_random_iterator<int>(std::begin(dataS)), non_random_iterator<int>(std::end(dataS)), i);

        CHECK_EQUAL(std::distance(std::begin(dataS), lb1), std::distance(std::begin(dataS), lb2));
      }
    }

    //*************************************************************************
    TEST(upper_bound_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        int* lb1 = std::upper_bound(std::begin(dataS), std::end(dataS), i);
        int* lb2 = etl::upper_bound(random_iterator<int>(std::begin(dataS)), random_iterator<int>(std::end(dataS)), i);

        CHECK_EQUAL(std::distance(std::begin(dataS), lb1), std::distance(std::begin(dataS), lb2));
      }
    }

    //*************************************************************************
    TEST(upper_bound_non_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        int* lb1 = std::upper_bound(std::begin(dataS), std::end(dataS), i);
        int* lb2 = etl::upper_bound(non_random_iterator<int>(std::begin(dataS)), non_random_iterator<int>(std::end(dataS)), i);

        CHECK_EQUAL(std::distance(std::begin(dataS), lb1), std::distance(std::begin(dataS), lb2));
      }
    }

    //*************************************************************************
    TEST(equal_range_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        ETL_OR_STD::pair<int*, int*>                                 lb1 = std::equal_range(std::begin(dataEQ), std::end(dataEQ), i);
        ETL_OR_STD::pair<random_iterator<int>, random_iterator<int>> lb2 =
          etl::equal_range(random_iterator<int>(std::begin(dataEQ)), random_iterator<int>(std::end(dataEQ)), i);

        CHECK_EQUAL(std::distance(std::begin(dataEQ), lb1.first), std::distance<int*>(std::begin(dataEQ), lb2.first));
        CHECK_EQUAL(std::distance(lb1.first, lb1.second), std::distance<int*>(lb2.first, lb2.second));
      }
    }

    //*************************************************************************
    TEST(equal_range_non_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        ETL_OR_STD::pair<int*, int*>                                         lb1 = std::equal_range(std::begin(dataEQ), std::end(dataEQ), i);
        ETL_OR_STD::pair<non_random_iterator<int>, non_random_iterator<int>> lb2 =
          etl::equal_range(non_random_iterator<int>(std::begin(dataEQ)), non_random_iterator<int>(std::end(dataEQ)), i);

        CHECK_EQUAL(std::distance(std::begin(dataEQ), lb1.first), std::distance<int*>(std::begin(dataEQ), lb2.first));
        CHECK_EQUAL(std::distance(lb1.first, lb1.second), std::distance<int*>(lb2.first, lb2.second));
      }
    }

    //*************************************************************************
    TEST(binary_search_random_iterator)
    {
      for (int i = 0; i < 11; ++i)
      {
        bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i);
        bool result   = etl::binary_search(std::begin(dataS), std::end(dataS), i);

        CHECK_EQUAL(expected, result);
      }
    }

    //*************************************************************************
    TEST(binary_search_with_compare)
    {
      for (int i = 0; i < 11; ++i)
      {
        bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i, etl::less<int>());
        bool result   = etl::binary_search(std::begin(dataS), std::end(dataS), i, etl::less<int>());

        CHECK_EQUAL(expected, result);
      }
    }

    //*************************************************************************
    TEST(binary_search_duplicates)
    {
      for (int i = 0; i < 11; ++i)
      {
        bool expected = std::binary_search(std::begin(dataEQ), std::end(dataEQ), i);
        bool result   = etl::binary_search(std::begin(dataEQ), std::end(dataEQ), i);

        CHECK_EQUAL(expected, result);
      }
    }

    //*************************************************************************
    TEST(binary_search_empty_range)
    {
      int empty[] = {0};

      bool result = etl::binary_search(std::begin(empty), std::begin(empty), 1);

      CHECK_EQUAL(false, result);
    }

    //*************************************************************************
    TEST(binary_search_single_element)
    {
      int single[] = {5};

      CHECK_EQUAL(true, etl::binary_search(std::begin(single), std::end(single), 5));
      CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 3));
      CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 7));
    }

    //*************************************************************************
    TEST(fill_non_char)
    {
      int data1[10];
      int data2[10];

      std::fill(std::begin(data1), std::end(data1), 0x12345678UL);
      etl::fill(std::begin(data2), std::end(data2), 0x12345678UL);

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(fill_char)
    {
      unsigned char data1[10];
      unsigned char data2[10];

      std::fill(std::begin(data1), std::end(data1), static_cast<unsigned char>(0x12U));
      etl::fill(std::begin(data2), std::end(data2), static_cast<unsigned char>(0x12U));

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(iter_swap_same_types)
    {
      int a = 1;
      int b = 2;

      etl::iter_swap(&a, &b);
      CHECK_EQUAL(2, a);
      CHECK_EQUAL(1, b);
    }

    //*************************************************************************
    TEST(iter_swap_differnt_types)
    {
      int  a = 1;
      long b = 2;

      etl::iter_swap(&a, &b);
      CHECK_EQUAL(2, a);
      CHECK_EQUAL(1, b);
    }

    //*************************************************************************
    TEST(swap_ranges_pod_pointer)
    {
      int data1[] = {1, 2, 3, 4, 5};
      int data2[] = {6, 7, 8, 9, 10};

      int expected1[] = {6, 7, 8, 9, 10};
      int expected2[] = {1, 2, 3, 4, 5};

      int* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2));

      CHECK_EQUAL(std::end(data2), result);

      bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1));
      CHECK(isEqual1);

      bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2));
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(swap_ranges_non_pod_pointer)
    {
      Data data1[] = {Data(1, 2), Data(3, 4), Data(5, 6)};
      Data data2[] = {Data(7, 8), Data(9, 10), Data(11, 12)};

      Data expected1[] = {Data(7, 8), Data(9, 10), Data(11, 12)};
      Data expected2[] = {Data(1, 2), Data(3, 4), Data(5, 6)};

      Data* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2));

      CHECK_EQUAL(std::end(data2), result);

      bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1));
      CHECK(isEqual1);

      bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2));
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(swap_ranges_non_random_iterator)
    {
      List data1 = {1, 2, 3, 4, 5};
      List data2 = {6, 7, 8, 9, 10};

      List expected1 = {6, 7, 8, 9, 10};
      List expected2 = {1, 2, 3, 4, 5};

      List::iterator result = etl::swap_ranges(data1.begin(), data1.end(), data2.begin());

      CHECK(data2.end() == result);

      bool isEqual1 = std::equal(data1.begin(), data1.end(), expected1.begin());
      CHECK(isEqual1);

      bool isEqual2 = std::equal(data2.begin(), data2.end(), expected2.begin());
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(swap_ranges_empty_range)
    {
      int data1[] = {1, 2, 3};
      int data2[] = {4, 5, 6};

      int expected1[] = {1, 2, 3};
      int expected2[] = {4, 5, 6};

      int* result = etl::swap_ranges(std::begin(data1), std::begin(data1), std::begin(data2));

      CHECK_EQUAL(std::begin(data2), result);

      bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1));
      CHECK(isEqual1);

      bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2));
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(swap_ranges_partial_range)
    {
      int data1[] = {1, 2, 3, 4, 5};
      int data2[] = {6, 7, 8, 9, 10};

      int expected1[] = {6, 7, 8, 4, 5};
      int expected2[] = {1, 2, 3, 9, 10};

      int* result = etl::swap_ranges(std::begin(data1), std::begin(data1) + 3, std::begin(data2));

      CHECK_EQUAL(std::begin(data2) + 3, result);

      bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1));
      CHECK(isEqual1);

      bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2));
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(swap_ranges_same_data)
    {
      int data1[]    = {1, 2, 3, 4, 5};
      int expected[] = {1, 2, 3, 4, 5};

      etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data1));

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(expected));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(swap_ranges_matches_std)
    {
      int data1_std[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int data2_std[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

      int data1_etl[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int data2_etl[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

      int* pstl = std::swap_ranges(std::begin(data1_std), std::end(data1_std), std::begin(data2_std));
      int* petl = etl::swap_ranges(std::begin(data1_etl), std::end(data1_etl), std::begin(data2_etl));

      using difference_type_t = std::iterator_traits<int*>::difference_type;

      difference_type_t dstl = std::distance(data2_std, pstl);
      difference_type_t detl = std::distance(data2_etl, petl);

      CHECK_EQUAL(dstl, detl);

      bool isEqual1 = std::equal(std::begin(data1_std), std::end(data1_std), std::begin(data1_etl));
      CHECK(isEqual1);

      bool isEqual2 = std::equal(std::begin(data2_std), std::end(data2_std), std::begin(data2_etl));
      CHECK(isEqual2);
    }

    //*************************************************************************
    TEST(equal)
    {
      CHECK(etl::equal(std::begin(dataV), std::end(dataV), std::begin(dataL)));
      CHECK(!etl::equal(std::begin(dataSL), std::end(dataSL), std::begin(dataL)));

      int small[] = {dataS[0]};
      CHECK(etl::equal(std::begin(dataV), std::end(dataV), std::begin(dataL), std::end(dataL)));
      CHECK(!etl::equal(std::begin(dataS), std::end(dataS), std::begin(small), std::end(small)));
    }

    //*************************************************************************
    TEST(lexicographical_compare)
    {
      std::string text1("Hello World");
      std::string text2("Hello Xorld");

      bool t1 = std::lexicographical_compare(text1.begin(), text1.end(), text2.begin(), text2.begin() + 7);
      bool t2 = etl::lexicographical_compare(text1.begin(), text1.end(), text2.begin(), text2.begin() + 7);

      CHECK(t1 == t2);
    }

    //*************************************************************************
    TEST(lexicographical_compare_greater)
    {
      std::string text1("Hello World");
      std::string text2("Hello Xorld");

      bool t1 = std::lexicographical_compare(text1.begin(), text1.end(), text2.begin(), text2.begin() + 7, Greater());
      bool t2 = etl::lexicographical_compare(text1.begin(), text1.end(), text2.begin(), text2.begin() + 7, Greater());

      CHECK(t1 == t2);
    }

    //*************************************************************************
    TEST(search)
    {
      std::string haystack = "ABCDFEGHIJKLMNOPQRSTUVWXYZ";
      std::string needle   = "KLMNO";

      std::string::iterator itr1 = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.begin());
      std::string::iterator itr2 = etl::search(haystack.begin(), haystack.end(), needle.begin(), needle.begin());

      CHECK(itr1 == itr2);
    }

    //*************************************************************************
    TEST(search_predicate)
    {
      std::string haystack = "ABCDFEGHIJKLMNOPQRSTUVWXYZ";
      std::string needle   = "KLMNO";

      std::string::iterator itr1 = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.begin(), std::equal_to<char>());
      std::string::iterator itr2 = etl::search(haystack.begin(), haystack.end(), needle.begin(), needle.begin(), std::equal_to<char>());

      CHECK(itr1 == itr2);
    }

    //*************************************************************************
    TEST(find_end_default)
    {
      int data[]    = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
      int pattern[] = {1, 2, 3};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_predicate)
    {
      int data[]    = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
      int pattern[] = {1, 2, 3};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to<int>());
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_single_occurrence)
    {
      int data[]    = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int pattern[] = {5, 6, 7};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_no_match)
    {
      int data[]    = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int pattern[] = {11, 12};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_empty_sequence)
    {
      int data[]    = {1, 2, 3, 4, 5};
      int pattern[] = {0};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_pattern_at_end)
    {
      int data[]    = {1, 2, 3, 4, 5, 6, 7};
      int pattern[] = {5, 6, 7};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_pattern_at_start)
    {
      int data[]    = {1, 2, 3, 4, 5, 6, 7};
      int pattern[] = {1, 2, 3};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_entire_range_matches)
    {
      int data[]    = {1, 2, 3, 4, 5};
      int pattern[] = {1, 2, 3, 4, 5};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_overlapping_occurrences)
    {
      int data[]    = {1, 1, 1, 1, 1};
      int pattern[] = {1, 1};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_single_element_pattern)
    {
      int data[]    = {1, 2, 3, 2, 5, 2, 7};
      int pattern[] = {2};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(find_end_non_random_iterator)
    {
      int data_array[]    = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
      int pattern_array[] = {1, 2, 3};

      List data(std::begin(data_array), std::end(data_array));
      List pattern(std::begin(pattern_array), std::end(pattern_array));

      List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to<int>());
      List::iterator result   = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to<int>());

      CHECK(expected == result);
    }

    //*************************************************************************
    TEST(find_end_non_random_iterator_predicate)
    {
      int data_array[]    = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
      int pattern_array[] = {1, 2, 3};

      List data(std::begin(data_array), std::end(data_array));
      List pattern(std::begin(pattern_array), std::end(pattern_array));

      List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to<int>());
      List::iterator result   = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to<int>());

      CHECK(expected == result);
    }

    //*************************************************************************
    TEST(find_end_pattern_longer_than_data)
    {
      int data[]    = {1, 2, 3};
      int pattern[] = {1, 2, 3, 4, 5};

      int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));
      int* result   = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_default)
    {
      int data[] = {1, 2, 3, 3, 4, 5};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_predicate)
    {
      int data[] = {1, 2, 3, 3, 4, 5};

      int* expected = std::adjacent_find(std::begin(data), std::end(data), std::equal_to<int>());
      int* result   = etl::adjacent_find(std::begin(data), std::end(data), std::equal_to<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_no_match)
    {
      int data[] = {1, 2, 3, 4, 5, 6};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_at_beginning)
    {
      int data[] = {1, 1, 2, 3, 4, 5};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_at_end)
    {
      int data[] = {1, 2, 3, 4, 5, 5};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_multiple_pairs)
    {
      int data[] = {1, 1, 2, 2, 3, 3};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_single_element)
    {
      int data[] = {1};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_empty_range)
    {
      int data[] = {1};

      int* expected = std::adjacent_find(std::begin(data), std::begin(data));
      int* result   = etl::adjacent_find(std::begin(data), std::begin(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_all_same)
    {
      int data[] = {5, 5, 5, 5, 5};

      int* expected = std::adjacent_find(std::begin(data), std::end(data));
      int* result   = etl::adjacent_find(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_predicate_less)
    {
      int data[] = {5, 4, 3, 2, 1};

      int* expected = std::adjacent_find(std::begin(data), std::end(data), std::greater<int>());
      int* result   = etl::adjacent_find(std::begin(data), std::end(data), std::greater<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(adjacent_find_non_random_iterator)
    {
      int  data_array[] = {1, 2, 3, 3, 4, 5};
      List data(std::begin(data_array), std::end(data_array));

      List::iterator expected = std::adjacent_find(data.begin(), data.end());
      List::iterator result   = etl::adjacent_find(data.begin(), data.end());

      CHECK(expected == result);
    }

    //*************************************************************************
    TEST(adjacent_find_non_random_iterator_predicate)
    {
      int  data_array[] = {1, 2, 3, 3, 4, 5};
      List data(std::begin(data_array), std::end(data_array));

      List::iterator expected = std::adjacent_find(data.begin(), data.end(), std::equal_to<int>());
      List::iterator result   = etl::adjacent_find(data.begin(), data.end(), std::equal_to<int>());

      CHECK(expected == result);
    }

    //*************************************************************************
    TEST(adjacent_find_non_random_iterator_no_match)
    {
      int  data_array[] = {1, 2, 3, 4, 5, 6};
      List data(std::begin(data_array), std::end(data_array));

      List::iterator expected = std::adjacent_find(data.begin(), data.end());
      List::iterator result   = etl::adjacent_find(data.begin(), data.end());

      CHECK(expected == result);
    }

    //*************************************************************************
    TEST(heap)
    {
      using Vector = std::vector<std::string>;

      std::string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"), g("G"), h("H"), i("I"), j("J");

      Vector data1 = {a, b, c, d, e, f, g, h, i, j};
      Vector data2 = {a, b, c, d, e, f, g, h, i, j};

      std::make_heap(data1.begin(), data1.end());
      etl::make_heap(data2.begin(), data2.end());

      bool isEqual;

      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      std::pop_heap(data1.begin(), data1.end());
      etl::pop_heap(data2.begin(), data2.end());

      data1.pop_back();
      data2.pop_back();

      CHECK(std::is_heap(data1.begin(), data1.end()));
      CHECK(std::is_heap(data2.begin(), data2.end()));

      CHECK_EQUAL(data1.size(), data2.size());

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      data1.push_back(std::string("K"));
      data2.push_back(std::string("K"));

      std::push_heap(data1.begin(), data1.end());
      etl::push_heap(data2.begin(), data2.end());

      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(heap_movable)
    {
      ItemM a("A"), b("B"), c("C"), d("D"), e("E"), f("F"), g("G"), h("H"), i("I"), j("J");

      VectorM data1;
      data1.emplace_back(std::move(b));
      data1.emplace_back(std::move(a));
      data1.emplace_back(std::move(d));
      data1.emplace_back(std::move(c));
      data1.emplace_back(std::move(f));
      data1.emplace_back(std::move(e));
      data1.emplace_back(std::move(h));
      data1.emplace_back(std::move(g));
      data1.emplace_back(std::move(j));
      data1.emplace_back(std::move(i));

      VectorM data2;
      data2.emplace_back(ItemM("B"));
      data2.emplace_back(ItemM("A"));
      data2.emplace_back(ItemM("D"));
      data2.emplace_back(ItemM("C"));
      data2.emplace_back(ItemM("F"));
      data2.emplace_back(ItemM("E"));
      data2.emplace_back(ItemM("H"));
      data2.emplace_back(ItemM("G"));
      data2.emplace_back(ItemM("J"));
      data2.emplace_back(ItemM("I"));

      std::make_heap(data1.begin(), data1.end());
      etl::make_heap(data2.begin(), data2.end());

      bool isEqual;

      CHECK(std::is_heap(data1.begin(), data1.end()));
      CHECK(std::is_heap(data2.begin(), data2.end()));

      CHECK_EQUAL(data1.size(), data2.size());

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      std::pop_heap(data1.begin(), data1.end());
      etl::pop_heap(data2.begin(), data2.end());

      data1.pop_back();
      data2.pop_back();

      CHECK(std::is_heap(data1.begin(), data1.end()));
      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      data1.push_back(ItemM("K"));
      data2.push_back(ItemM("K"));

      std::push_heap(data1.begin(), data1.end());
      etl::push_heap(data2.begin(), data2.end());

      CHECK(std::is_heap(data2.begin(), data2.end()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(heap_greater)
    {
      Vector data1 = dataV;
      Vector data2 = dataV;

      std::make_heap(data1.begin(), data1.end(), Greater());
      etl::make_heap(data2.begin(), data2.end(), Greater());

      bool isEqual;

      CHECK(std::is_heap(data2.begin(), data2.end(), Greater()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      std::pop_heap(data1.begin(), data1.end(), Greater());
      etl::pop_heap(data2.begin(), data2.end(), Greater());

      data1.pop_back();
      data2.pop_back();

      CHECK(std::is_heap(data1.begin(), data1.end(), Greater()));
      CHECK(std::is_heap(data2.begin(), data2.end(), Greater()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      CHECK(std::is_heap(data2.begin(), data2.end(), Greater()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);

      data1.push_back(5);
      data2.push_back(5);

      std::push_heap(data1.begin(), data1.end(), Greater());
      etl::push_heap(data2.begin(), data2.end(), Greater());

      CHECK(std::is_heap(data2.begin(), data2.end(), Greater()));

      isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(is_heap_default_true)
    {
      std::vector<int> data = {9, 8, 7, 6, 5, 4, 3, 2, 1};
      std::make_heap(data.begin(), data.end());

      bool expected = std::is_heap(data.begin(), data.end());
      bool result   = etl::is_heap(data.begin(), data.end());

      CHECK_EQUAL(expected, result);
      CHECK(result);
    }

    //*************************************************************************
    TEST(is_heap_default_false)
    {
      std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      bool expected = std::is_heap(data.begin(), data.end());
      bool result   = etl::is_heap(data.begin(), data.end());

      CHECK_EQUAL(expected, result);
      CHECK(!result);
    }

    //*************************************************************************
    TEST(is_heap_compare_true)
    {
      std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      std::make_heap(data.begin(), data.end(), Greater());

      bool expected = std::is_heap(data.begin(), data.end(), Greater());
      bool result   = etl::is_heap(data.begin(), data.end(), Greater());

      CHECK_EQUAL(expected, result);
      CHECK(result);
    }

    //*************************************************************************
    TEST(is_heap_compare_false)
    {
      std::vector<int> data = {9, 8, 7, 6, 5, 4, 3, 2, 1};

      bool expected = std::is_heap(data.begin(), data.end(), Greater());
      bool result   = etl::is_heap(data.begin(), data.end(), Greater());

      CHECK_EQUAL(expected, result);
      CHECK(!result);
    }

    //*************************************************************************
    TEST(is_heap_empty)
    {
      std::vector<int> data;

      bool expected = std::is_heap(data.begin(), data.end());
      bool result   = etl::is_heap(data.begin(), data.end());

      CHECK_EQUAL(expected, result);
      CHECK(result);
    }

    //*************************************************************************
    TEST(is_heap_single_element)
    {
      std::vector<int> data = {42};

      bool expected = std::is_heap(data.begin(), data.end());
      bool result   = etl::is_heap(data.begin(), data.end());

      CHECK_EQUAL(expected, result);
      CHECK(result);
    }

    //*************************************************************************
    TEST(is_heap_two_elements)
    {
      std::vector<int> data1 = {5, 3};
      std::vector<int> data2 = {3, 5};

      CHECK_EQUAL(std::is_heap(data1.begin(), data1.end()), etl::is_heap(data1.begin(), data1.end()));
      CHECK_EQUAL(std::is_heap(data2.begin(), data2.end()), etl::is_heap(data2.begin(), data2.end()));
    }

    //*************************************************************************
    TEST(is_heap_pointer)
    {
      int data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};

      bool expected = std::is_heap(std::begin(data), std::end(data));
      bool result   = etl::is_heap(std::begin(data), std::end(data));

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(is_heap_after_make_heap)
    {
      // Test all permutations of a small dataset
      std::vector<int> data = {1, 2, 3, 4, 5};

      do {
        std::vector<int> test_data(data);
        etl::make_heap(test_data.begin(), test_data.end());
        CHECK(etl::is_heap(test_data.begin(), test_data.end()));
      } while (std::next_permutation(data.begin(), data.end()));
    }

    //*************************************************************************
    TEST(sort_heap_default)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> data2(data1);

      std::make_heap(data1.begin(), data1.end());
      etl::make_heap(data2.begin(), data2.end());

      std::sort_heap(data1.begin(), data1.end());
      etl::sort_heap(data2.begin(), data2.end());

      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);

      // Verify sorted ascending
      CHECK(std::is_sorted(data2.begin(), data2.end()));
    }

    //*************************************************************************
    TEST(sort_heap_compare)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> data2(data1);

      std::make_heap(data1.begin(), data1.end(), Greater());
      etl::make_heap(data2.begin(), data2.end(), Greater());

      std::sort_heap(data1.begin(), data1.end(), Greater());
      etl::sort_heap(data2.begin(), data2.end(), Greater());

      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);

      // Verify sorted descending
      CHECK(std::is_sorted(data2.begin(), data2.end(), Greater()));
    }

    //*************************************************************************
    TEST(sort_heap_empty)
    {
      std::vector<int> data;

      etl::sort_heap(data.begin(), data.end());

      CHECK(data.empty());
    }

    //*************************************************************************
    TEST(sort_heap_single_element)
    {
      std::vector<int> data = {42};

      etl::sort_heap(data.begin(), data.end());

      CHECK_EQUAL(1U, data.size());
      CHECK_EQUAL(42, data[0]);
    }

    //*************************************************************************
    TEST(sort_heap_pointer)
    {
      int data1[] = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      int data2[] = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};

      std::make_heap(std::begin(data1), std::end(data1));
      etl::make_heap(std::begin(data2), std::end(data2));

      std::sort_heap(std::begin(data1), std::end(data1));
      etl::sort_heap(std::begin(data2), std::end(data2));

      bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(sort_heap_all_permutations)
    {
      std::vector<int> initial = {1, 2, 3, 4, 5};

      do {
        std::vector<int> data1(initial);
        std::vector<int> data2(initial);

        std::make_heap(data1.begin(), data1.end());
        etl::make_heap(data2.begin(), data2.end());

        std::sort_heap(data1.begin(), data1.end());
        etl::sort_heap(data2.begin(), data2.end());

        bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(isEqual);
      } while (std::next_permutation(initial.begin(), initial.end()));
    }

    //*************************************************************************
    TEST(sort_heap_duplicates)
    {
      std::vector<int> data1 = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
      std::vector<int> data2(data1);

      std::make_heap(data1.begin(), data1.end());
      etl::make_heap(data2.begin(), data2.end());

      std::sort_heap(data1.begin(), data1.end());
      etl::sort_heap(data2.begin(), data2.end());

      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(partial_sort_default)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 5, data1.end());
      etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end());

      // The first 5 elements should be sorted and match std
      bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin());
      CHECK(isEqual);

      // Verify sorted range
      CHECK(std::is_sorted(data2.begin(), data2.begin() + 5));
    }

    //*************************************************************************
    TEST(partial_sort_compare)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 5, data1.end(), Greater());
      etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end(), Greater());

      // The first 5 elements should be sorted descending and match std
      bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin());
      CHECK(isEqual);

      // Verify sorted range (descending)
      CHECK(std::is_sorted(data2.begin(), data2.begin() + 5, Greater()));
    }

    //*************************************************************************
    TEST(partial_sort_empty)
    {
      std::vector<int> data;

      etl::partial_sort(data.begin(), data.begin(), data.end());

      CHECK(data.empty());
    }

    //*************************************************************************
    TEST(partial_sort_middle_equals_first)
    {
      std::vector<int> data = {5, 3, 8, 1, 9};
      std::vector<int> original(data);

      etl::partial_sort(data.begin(), data.begin(), data.end());

      // Nothing should change when middle == first
      bool isEqual = std::equal(data.begin(), data.end(), original.begin());
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(partial_sort_middle_equals_last)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.end(), data1.end());
      etl::partial_sort(data2.begin(), data2.end(), data2.end());

      // Full sort
      bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(data2.begin(), data2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_single_element)
    {
      std::vector<int> data = {42};

      etl::partial_sort(data.begin(), data.end(), data.end());

      CHECK_EQUAL(1U, data.size());
      CHECK_EQUAL(42, data[0]);
    }

    //*************************************************************************
    TEST(partial_sort_first_one)
    {
      std::vector<int> data1 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 1, data1.end());
      etl::partial_sort(data2.begin(), data2.begin() + 1, data2.end());

      // The first element should be the minimum
      CHECK_EQUAL(data1[0], data2[0]);
      CHECK_EQUAL(1, data2[0]);
    }

    //*************************************************************************
    TEST(partial_sort_duplicates)
    {
      std::vector<int> data1 = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 6, data1.end());
      etl::partial_sort(data2.begin(), data2.begin() + 6, data2.end());

      bool isEqual = std::equal(data1.begin(), data1.begin() + 6, data2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(data2.begin(), data2.begin() + 6));
    }

    //*************************************************************************
    TEST(partial_sort_already_sorted)
    {
      std::vector<int> data1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 5, data1.end());
      etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end());

      bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(data2.begin(), data2.begin() + 5));
    }

    //*************************************************************************
    TEST(partial_sort_reverse_sorted)
    {
      std::vector<int> data1 = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
      std::vector<int> data2(data1);

      std::partial_sort(data1.begin(), data1.begin() + 5, data1.end());
      etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end());

      bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(data2.begin(), data2.begin() + 5));
    }

    //*************************************************************************
    TEST(partial_sort_pointer)
    {
      int data1[] = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      int data2[] = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};

      std::partial_sort(std::begin(data1), std::begin(data1) + 5, std::end(data1));
      etl::partial_sort(std::begin(data2), std::begin(data2) + 5, std::end(data2));

      bool isEqual = std::equal(std::begin(data1), std::begin(data1) + 5, std::begin(data2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(partial_sort_all_permutations)
    {
      std::vector<int> initial = {1, 2, 3, 4, 5};

      do {
        std::vector<int> data1(initial);
        std::vector<int> data2(initial);

        std::partial_sort(data1.begin(), data1.begin() + 3, data1.end());
        etl::partial_sort(data2.begin(), data2.begin() + 3, data2.end());

        bool isEqual = std::equal(data1.begin(), data1.begin() + 3, data2.begin());
        CHECK(isEqual);
      } while (std::next_permutation(initial.begin(), initial.end()));
    }

    //*************************************************************************
    TEST(partial_sort_all_equal)
    {
      std::vector<int> data = {5, 5, 5, 5, 5, 5};

      etl::partial_sort(data.begin(), data.begin() + 3, data.end());

      CHECK(std::is_sorted(data.begin(), data.begin() + 3));

      for (size_t i = 0; i < data.size(); ++i)
      {
        CHECK_EQUAL(5, data[i]);
      }
    }

    //*************************************************************************
    TEST(partial_sort_copy_default)
    {
      std::vector<int> input = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_compare)
    {
      std::vector<int> input = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end(), Greater());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end(), Greater());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end(), Greater()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_empty_input)
    {
      std::vector<int> input;
      std::vector<int> output(5, 99);

      auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end());

      CHECK(result == output.begin());

      // Output should be unchanged
      for (size_t i = 0; i < output.size(); ++i)
      {
        CHECK_EQUAL(99, output[i]);
      }
    }

    //*************************************************************************
    TEST(partial_sort_copy_empty_output)
    {
      std::vector<int> input = {5, 3, 8, 1, 9};
      std::vector<int> output;

      auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end());

      CHECK(result == output.begin());
    }

    //*************************************************************************
    TEST(partial_sort_copy_output_larger_than_input)
    {
      std::vector<int> input = {5, 3, 1};
      std::vector<int> output1(6, 99);
      std::vector<int> output2(6, 99);

      auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      CHECK_EQUAL(std::distance(output1.begin(), result1), std::distance(output2.begin(), result2));

      bool isEqual = std::equal(output1.begin(), result1, output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), result2));
    }

    //*************************************************************************
    TEST(partial_sort_copy_output_smaller_than_input)
    {
      std::vector<int> input = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> output1(3);
      std::vector<int> output2(3);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_output_same_size_as_input)
    {
      std::vector<int> input = {5, 3, 8, 1, 9};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      CHECK(result1 == output1.end());
      CHECK(result2 == output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_single_element_input)
    {
      std::vector<int> input = {42};
      std::vector<int> output(3, 0);

      auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end());

      CHECK(result == output.begin() + 1);
      CHECK_EQUAL(42, output[0]);
    }

    //*************************************************************************
    TEST(partial_sort_copy_single_element_output)
    {
      std::vector<int> input = {5, 3, 8, 1, 9};
      std::vector<int> output1(1);
      std::vector<int> output2(1);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      CHECK_EQUAL(output1[0], output2[0]);
      CHECK_EQUAL(1, output2[0]);
    }

    //*************************************************************************
    TEST(partial_sort_copy_duplicates)
    {
      std::vector<int> input = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
      std::vector<int> output1(6);
      std::vector<int> output2(6);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_already_sorted)
    {
      std::vector<int> input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_reverse_sorted)
    {
      std::vector<int> input = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(partial_sort_copy_pointer)
    {
      int input[]    = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      int output1[5] = {};
      int output2[5] = {};

      std::partial_sort_copy(std::begin(input), std::end(input), std::begin(output1), std::end(output1));
      etl::partial_sort_copy(std::begin(input), std::end(input), std::begin(output2), std::end(output2));

      bool isEqual = std::equal(std::begin(output1), std::end(output1), std::begin(output2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(partial_sort_copy_all_equal)
    {
      std::vector<int> input = {5, 5, 5, 5, 5, 5};
      std::vector<int> output(3);

      etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end());

      CHECK(std::is_sorted(output.begin(), output.end()));

      for (size_t i = 0; i < output.size(); ++i)
      {
        CHECK_EQUAL(5, output[i]);
      }
    }

    //*************************************************************************
    TEST(partial_sort_copy_input_not_modified)
    {
      std::vector<int> input = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> original(input);
      std::vector<int> output(5);

      etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end());

      // Input should not be modified
      bool isEqual = std::equal(input.begin(), input.end(), original.begin());
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(partial_sort_copy_from_list)
    {
      std::list<int>   input = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
      std::vector<int> output1(5);
      std::vector<int> output2(5);

      std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end());
      etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end());

      bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin());
      CHECK(isEqual);

      CHECK(std::is_sorted(output2.begin(), output2.end()));
    }

    //*************************************************************************
    TEST(find)
    {
      int* itr1 = std::find(std::begin(dataA), std::end(dataA), 5);
      int* itr2 = etl::find(std::begin(dataA), std::end(dataA), 5);

      CHECK(itr1 == itr2);
    }

    //*************************************************************************
    TEST(find_if)
    {
      struct predicate
      {
        bool operator()(int i) const
        {
          return (i == 5);
        }
      };

      int* itr1 = std::find_if(std::begin(dataA), std::end(dataA), predicate());
      int* itr2 = etl::find_if(std::begin(dataA), std::end(dataA), predicate());

      CHECK(itr1 == itr2);
    }

    //*************************************************************************
    TEST(count)
    {
      size_t c1 = static_cast<size_t>(std::count(std::begin(dataEQ), std::end(dataEQ), 5));
      size_t c2 = static_cast<size_t>(etl::count(std::begin(dataEQ), std::end(dataEQ), 5));

      CHECK(c1 == c2);
    }

    //*************************************************************************
    TEST(count_if)
    {
      struct predicate
      {
        bool operator()(int i) const
        {
          return (i == 5);
        }
      };

      size_t c1 = static_cast<size_t>(std::count_if(std::begin(dataEQ), std::end(dataEQ), predicate()));
      size_t c2 = static_cast<size_t>(etl::count_if(std::begin(dataEQ), std::end(dataEQ), predicate()));

      CHECK(c1 == c2);
    }

    //*************************************************************************
    TEST(fill_n)
    {
      (void)std::fill_n(std::begin(dataD1), SIZE, 5);
      int* p2 = etl::fill_n(std::begin(dataD2), SIZE, 5);

      CHECK(p2 == std::end(dataD2));

      bool isEqual = std::equal(std::begin(dataD1), std::end(dataD1), std::begin(dataD2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(transform1)
    {
      struct Function
      {
        int operator()(int d) const
        {
          return d * 2;
        }
      };

      (void)std::transform(std::begin(dataS), std::end(dataS), std::begin(dataD1), Function());
      int* p2 = etl::transform(std::begin(dataS), std::end(dataS), std::begin(dataD2), Function());

      CHECK(p2 == std::end(dataD2));

      bool isEqual = std::equal(std::begin(dataD1), std::end(dataD1), std::begin(dataD2));
      CHECK(isEqual);
    }

    //*************************************************************************
    TEST(move)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;

      // Create some data.
      std::unique_ptr<uint32_t> p1(new uint32_t(1U));
      std::unique_ptr<uint32_t> p2(new uint32_t(2U));
      std::unique_ptr<uint32_t> p3(new uint32_t(3U));
      std::unique_ptr<uint32_t> p4(new uint32_t(4U));
      std::unique_ptr<uint32_t> p5(new uint32_t(5U));

      // Fill data1.
      data1.push_back(std::move(p1));
      data1.push_back(std::move(p2));
      data1.push_back(std::move(p3));
      data1.push_back(std::move(p4));
      data1.push_back(std::move(p5));

      Data data2;

      // Move to data2.
      etl::move(data1.begin(), data1.end(), std::back_inserter(data2));

      // Old data now empty.
      CHECK(!bool(p1));
      CHECK(!bool(p2));
      CHECK(!bool(p3));
      CHECK(!bool(p4));
      CHECK(!bool(p5));

      CHECK_EQUAL(1U, *(data2[0]));
      CHECK_EQUAL(2U, *(data2[1]));
      CHECK_EQUAL(3U, *(data2[2]));
      CHECK_EQUAL(4U, *(data2[3]));
      CHECK_EQUAL(5U, *(data2[4]));
    }

    //*************************************************************************
    TEST(move_backward)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;

      // Create some data.
      std::unique_ptr<uint32_t> p1(new uint32_t(1U));
      std::unique_ptr<uint32_t> p2(new uint32_t(2U));
      std::unique_ptr<uint32_t> p3(new uint32_t(3U));
      std::unique_ptr<uint32_t> p4(new uint32_t(4U));
      std::unique_ptr<uint32_t> p5(new uint32_t(5U));

      // Fill data1.
      data1.push_back(std::move(p1));
      data1.push_back(std::move(p2));
      data1.push_back(std::move(p3));
      data1.push_back(std::move(p4));
      data1.push_back(std::move(p5));

      Data data2;

      // Create some data.
      std::unique_ptr<uint32_t> p6(new uint32_t(6U));
      std::unique_ptr<uint32_t> p7(new uint32_t(7U));
      std::unique_ptr<uint32_t> p8(new uint32_t(8U));
      std::unique_ptr<uint32_t> p9(new uint32_t(9U));
      std::unique_ptr<uint32_t> p10(new uint32_t(10U));

      // Fill data2.
      data2.push_back(std::move(p6));
      data2.push_back(std::move(p7));
      data2.push_back(std::move(p8));
      data2.push_back(std::move(p9));
      data2.push_back(std::move(p10));

      // Overwrite data2 with data1.
      etl::move_backward(data1.begin(), data1.end(), data2.end());

      // Old data now empty.
      CHECK(!bool(p1));
      CHECK(!bool(p2));
      CHECK(!bool(p3));
      CHECK(!bool(p4));
      CHECK(!bool(p5));

      CHECK_EQUAL(1U, *(data2[0]));
      CHECK_EQUAL(2U, *(data2[1]));
      CHECK_EQUAL(3U, *(data2[2]));
      CHECK_EQUAL(4U, *(data2[3]));
      CHECK_EQUAL(5U, *(data2[4]));
    }

    //*************************************************************************
    TEST(move_s_random_iterator_same_size)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(4U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(5U)));

      Data data2(5);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.end() == result);

      CHECK_EQUAL(1U, *(data2[0]));
      CHECK_EQUAL(2U, *(data2[1]));
      CHECK_EQUAL(3U, *(data2[2]));
      CHECK_EQUAL(4U, *(data2[3]));
      CHECK_EQUAL(5U, *(data2[4]));
    }

    //*************************************************************************
    TEST(move_s_random_iterator_destination_smaller)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(4U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(5U)));

      Data data2(3);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.end() == result);

      CHECK_EQUAL(1U, *(data2[0]));
      CHECK_EQUAL(2U, *(data2[1]));
      CHECK_EQUAL(3U, *(data2[2]));
    }

    //*************************************************************************
    TEST(move_s_random_iterator_source_smaller)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));

      Data data2(5);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.begin() + 3 == result);

      CHECK_EQUAL(1U, *(data2[0]));
      CHECK_EQUAL(2U, *(data2[1]));
      CHECK_EQUAL(3U, *(data2[2]));
    }

    //*************************************************************************
    TEST(move_s_non_random_iterator_same_size)
    {
      typedef std::list<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(4U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(5U)));

      Data data2(5);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.end() == result);

      Data::iterator itr = data2.begin();
      CHECK_EQUAL(1U, **(itr++));
      CHECK_EQUAL(2U, **(itr++));
      CHECK_EQUAL(3U, **(itr++));
      CHECK_EQUAL(4U, **(itr++));
      CHECK_EQUAL(5U, **(itr++));
    }

    //*************************************************************************
    TEST(move_s_non_random_iterator_destination_smaller)
    {
      typedef std::list<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(4U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(5U)));

      Data data2(3);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.end() == result);

      Data::iterator itr = data2.begin();
      CHECK_EQUAL(1U, **(itr++));
      CHECK_EQUAL(2U, **(itr++));
      CHECK_EQUAL(3U, **(itr++));
    }

    //*************************************************************************
    TEST(move_s_non_random_iterator_source_smaller)
    {
      typedef std::list<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));

      Data data2(5);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      Data::iterator expected_pos = data2.begin();
      std::advance(expected_pos, 3);
      CHECK(expected_pos == result);

      Data::iterator itr = data2.begin();
      CHECK_EQUAL(1U, **(itr++));
      CHECK_EQUAL(2U, **(itr++));
      CHECK_EQUAL(3U, **(itr++));
    }

    //*************************************************************************
    TEST(move_s_empty_source)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;
      Data data2(3);

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.begin() == result);
    }

    //*************************************************************************
    TEST(move_s_empty_destination)
    {
      typedef std::vector<std::unique_ptr<unsigned>> Data;

      Data data1;
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(1U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(2U)));
      data1.push_back(std::unique_ptr<uint32_t>(new uint32_t(3U)));

      Data data2;

      Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end());

      CHECK(data2.begin() == result);
    }

    //*************************************************************************
    TEST(move_s_pod_random_iterator)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int data2[] = {1, 2, 3, 4, 5};

      int out1[10];
      int out2[5];

      int check1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int check2[] = {1, 2, 3, 4, 5};
      int check3[] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0};

      int* result;

      // Same size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2));
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Source smaller.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::move_s(std::begin(data2), std::end(data2), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::begin(out1) + 5, result);
      is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(rotate_pod)
    {
      std::vector<int> initial_data = {1, 2, 3, 4, 5, 6, 7};

      for (size_t i = 0UL; i < initial_data.size(); ++i)
      {
        std::vector<int> data1(initial_data);
        std::vector<int> data2(initial_data);

        std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());
        etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size());

        bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
        CHECK(isEqual);
      }
    }

    //*************************************************************************
    TEST(rotate_non_pod)
    {
      std::vector<NDC> initial_data = {NDC(1), NDC(2), NDC(3), NDC(4), NDC(5), NDC(6), NDC(7)};

      for (size_t i = 0UL; i < initial_data.size(); ++i)
      {
        std::vector<NDC> data1(initial_data);
        std::vector<NDC> data2(initial_data);

        std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());
        etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size());

        bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
        CHECK(isEqual);
      }
    }

    //*************************************************************************
    TEST(rotate_return_value)
    {
      // Verify that etl::rotate returns the same iterator as std::rotate
      // in all cases, including the degenerate first==middle and middle==last
      // cases.
      std::vector<int> initial_data = {1, 2, 3, 4, 5};

      for (size_t i = 0UL; i <= initial_data.size(); ++i)
      {
#include "etl/private/diagnostic_null_dereference_push.h"
        std::vector<int> data1(initial_data);
        std::vector<int> data2(initial_data);
#include "etl/private/diagnostic_pop.h"

        auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());
        auto etl_result = etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size());

        // Check that the return value offset matches
        ptrdiff_t std_offset = std_result - data1.data();
        ptrdiff_t etl_offset = etl_result - data2.data();
        CHECK_EQUAL(std_offset, etl_offset);
      }

      // Explicitly test first == middle (empty left half): should return last
      {
        std::vector<int> data   = {1, 2, 3};
        auto             result = etl::rotate(data.data(), data.data(), data.data() + data.size());
        CHECK(result == data.data() + data.size());
      }

      // Explicitly test middle == last (empty right half): should return first
      {
        std::vector<int> data   = {1, 2, 3};
        auto             result = etl::rotate(data.data(), data.data() + data.size(), data.data() + data.size());
        CHECK(result == data.data());
      }
    }

    //*************************************************************************
    TEST(rotate_return_value_non_random_iterator)
    {
      // Verify that etl::rotate returns the correct iterator when called with
      // non-random (bidirectional) iterators, exercising rotate_general for
      // bidirectional iterators rather than the random-access overload.
      std::vector<int> initial_data = {1, 2, 3, 4, 5};

      for (size_t i = 0UL; i <= initial_data.size(); ++i)
      {
#include "etl/private/diagnostic_null_dereference_push.h"
        std::vector<int> data1(initial_data);
        std::vector<int> data2(initial_data);
#include "etl/private/diagnostic_pop.h"

        auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());

        non_random_iterator<int> nr_first(data2.data());
        non_random_iterator<int> nr_middle(data2.data() + i);
        non_random_iterator<int> nr_last(data2.data() + data2.size());
        auto                     etl_result = etl::rotate(nr_first, nr_middle, nr_last);

        // Check that the data was rotated correctly
        bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
        CHECK(isEqual);

        // Check that the return value offset matches
        ptrdiff_t std_offset = std_result - data1.data();
        ptrdiff_t etl_offset = etl_result.ptr - data2.data();
        CHECK_EQUAL(std_offset, etl_offset);
      }

      // Explicitly test first == middle (empty left half): should return last
      {
        std::vector<int>         data = {1, 2, 3};
        non_random_iterator<int> nr_first(data.data());
        non_random_iterator<int> nr_middle(data.data());
        non_random_iterator<int> nr_last(data.data() + data.size());
        auto                     result = etl::rotate(nr_first, nr_middle, nr_last);
        CHECK(result.ptr == data.data() + data.size());
      }

      // Explicitly test middle == last (empty right half): should return first
      {
        std::vector<int>         data = {1, 2, 3};
        non_random_iterator<int> nr_first(data.data());
        non_random_iterator<int> nr_middle(data.data() + data.size());
        non_random_iterator<int> nr_last(data.data() + data.size());
        auto                     result = etl::rotate(nr_first, nr_middle, nr_last);
        CHECK(result.ptr == data.data());
      }
    }

    //*************************************************************************
    TEST(any_of)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      bool expected = std::any_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      bool result   = etl::any_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(expected, result);

      expected = std::any_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      result   = etl::any_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(all_of)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      bool expected = std::all_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      bool result   = etl::all_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      CHECK_EQUAL(expected, result);

      expected = std::all_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      result   = etl::all_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(none_of)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      bool expected = std::none_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 8));
      bool result   = etl::none_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 8));
      CHECK_EQUAL(expected, result);

      expected = std::none_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      result   = etl::none_of(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(expected, result);
    }

    struct Compare
    {
      bool operator()(int a, int b) const
      {
        return a == b;
      }
    };

    //*************************************************************************
    TEST(is_permutation)
    {
      int data1[]           = {1, 2, 3, 4, 5, 6, 7, 8};
      int permutation[]     = {1, 3, 2, 4, 7, 6, 5, 8};
      int not_permutation[] = {1, 2, 3, 4, 5, 6, 7, 7};

      bool is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation));
      CHECK(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation));
      CHECK(!is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), etl::equal_to<int>());
      CHECK(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), etl::equal_to<int>());
      CHECK(!is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), std::end(permutation));
      CHECK(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), std::end(not_permutation));
      CHECK(!is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), std::end(permutation), etl::equal_to<int>());
      CHECK(is_permutation);

      is_permutation =
        etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), std::end(not_permutation), etl::equal_to<int>());
      CHECK(!is_permutation);
    }

    //*************************************************************************
    TEST(is_permutation_different_lengths)
    {
      int shorter[] = {1, 2};
      int longer[]  = {1, 2, 3};

      // Four-iterator: range2 longer than range1 (extra elements only in
      // range2)
      bool result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer));
      CHECK(!result);

      // Four-iterator: range1 longer than range2
      result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter));
      CHECK(!result);

      // Four-iterator with predicate: range2 longer than range1
      result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer), etl::equal_to<int>());
      CHECK(!result);

      // Four-iterator with predicate: range1 longer than range2
      result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter), etl::equal_to<int>());
      CHECK(!result);
    }

    //*************************************************************************
    TEST(is_partitioned)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8};

      bool expected = std::is_partitioned(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      bool result   = etl::is_partitioned(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(expected, result);

      std::partition(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));

      expected = std::is_partitioned(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      result   = etl::is_partitioned(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(partition_point)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8};

      std::partition(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));

      int* partition1 = std::partition_point(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      int* partition2 = etl::partition_point(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(std::distance(std::begin(data1), partition1), std::distance(std::begin(data1), partition2));

      std::partition(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 8));

      partition1 = std::partition_point(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      partition2 = etl::partition_point(std::begin(data1), std::end(data1), std::bind(std::greater<int>(), std::placeholders::_1, 0));
      CHECK_EQUAL(std::distance(std::begin(data1), partition1), std::distance(std::begin(data1), partition2));
    }

    //*************************************************************************
    TEST(partition_copy)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8};
      int data2[] = {0, 0, 0, 0, 0, 0, 0, 0};
      int data3[] = {0, 0, 0, 0, 0, 0, 0, 0};
      int data4[] = {0, 0, 0, 0, 0, 0, 0, 0};
      int data5[] = {0, 0, 0, 0, 0, 0, 0, 0};

      bool complete = false;

      while (!complete)
      {
        std::partition_copy(std::begin(data1), std::end(data1), std::begin(data2), std::begin(data3),
                            std::bind(std::greater<int>(), std::placeholders::_1, 4));
        etl::partition_copy(std::begin(data1), std::end(data1), std::begin(data4), std::begin(data5),
                            std::bind(std::greater<int>(), std::placeholders::_1, 4));

        bool are_equal;

        are_equal = std::equal(std::begin(data2), std::end(data2), std::begin(data4));
        CHECK(are_equal);

        are_equal = std::equal(std::begin(data3), std::end(data3), std::begin(data5));
        CHECK(are_equal);

        complete = !std::next_permutation(std::begin(data1), std::end(data1));
      }
    }

    //*************************************************************************
    TEST(partition_move)
    {
      std::array<int, 8> indexes = {1, 2, 3, 4, 5, 6, 7, 8};

      TestDataM<int> std_input[] = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};
      TestDataM<int> etl_input[] = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};
      TestDataM<int> std_true[]  = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};
      TestDataM<int> std_false[] = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};
      TestDataM<int> etl_true[]  = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};
      TestDataM<int> etl_false[] = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                    TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};

      auto compare = [](const TestDataM<int>& element)
      {
        return (element.value > 4);
      };

      bool complete = false;

      while (!complete)
      {
        // Rebuild the input arrays.
        for (size_t i = 0; i < indexes.size(); ++i)
        {
          std_input[i].value = indexes[i];
          etl_input[i].value = indexes[i];
        }

        // Simulate what etl::partition_move should do using stable_partition and move.
        auto boundary = std::stable_partition(std::begin(std_input), std::end(std_input), compare);
        std::move(std::begin(std_input), boundary, std::begin(std_true));
        std::move(boundary, std::end(std_input), std::begin(std_false));

        // Execute etl::partition_move.
        etl::partition_move(std::begin(etl_input), std::end(etl_input), std::begin(etl_true), std::begin(etl_false), compare);
        bool are_equal;

        are_equal = std::equal(std::begin(std_true), std::end(std_true), std::begin(etl_true));
        CHECK(are_equal);

        are_equal = std::equal(std::begin(std_false), std::end(std_false), std::begin(etl_false));
        CHECK(are_equal);

        complete = !std::next_permutation(std::begin(indexes), std::end(indexes));
      }
    }

    //*************************************************************************
    TEST(find_if_not)
    {
      int data1[] = {1, 2, 3, 5, 6, 7, 8};

      // Find the element not less than 4.
      int* p = etl::find_if_not(std::begin(data1), std::end(data1), std::bind(std::less<int>(), std::placeholders::_1, 4));
      CHECK_EQUAL(5, *p);
    }

    //*************************************************************************
    TEST(copy_4_parameter_random_iterator)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int data2[] = {1, 2, 3, 4, 5};

      int out1[10];
      int out2[5];

      int check1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int check2[] = {1, 2, 3, 4, 5};
      int check3[] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0};

      int* result;

      // Same size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result = etl::copy_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2));
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Source smaller.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_s(std::begin(data2), std::end(data2), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::begin(out1) + 5, result);
      is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_4_parameter_non_random_iterator)
    {
      std::list<int> data1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      std::list<int> data2 = {1, 2, 3, 4, 5};

      int out1[10];
      int out2[5];

      int check1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int check2[] = {1, 2, 3, 4, 5};
      int check3[] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0};

      int* result;

      // Same size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result = etl::copy_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2));
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Source smaller.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_s(std::begin(data2), std::end(data2), std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::begin(out1) + 5, result);
      is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_n_4_parameter)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      int out1[10];
      int out2[5];

      int check1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int check2[] = {1, 2, 3, 4, 5};
      int check3[] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0};

      int* result;

      // Same size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_n_s(std::begin(data1), 10, std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result = etl::copy_n_s(std::begin(data1), 10, std::begin(out2), std::end(out2));
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Source smaller.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_n_s(std::begin(data1), 5, std::begin(out1), std::end(out1));
      CHECK_EQUAL(std::begin(out1) + 5, result);
      is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_2n_4_parameter)
    {
      int data1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      int out1[10];
      int out2[5];

      int check1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int check2[] = {1, 2, 3, 4, 5};
      int check3[] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0};

      int* result;

      // Same size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_n_s(std::begin(data1), 10, std::begin(out1), 10);
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result = etl::copy_n_s(std::begin(data1), 10, std::begin(out2), 5);
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Source smaller.
      std::fill(std::begin(out1), std::end(out1), 0);
      result = etl::copy_n_s(std::begin(data1), 5, std::begin(out1), 10);
      CHECK_EQUAL(std::begin(out1) + 5, result);
      is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_n_if)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int data2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int data3[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      // Copy everything less than 5.
      int* pout = data2;
      for (int* pin = std::begin(data1); pin != std::begin(data1) + 6; ++pin)
      {
        if (*pin < 5)
        {
          *pout++ = *pin;
        }
      }
      etl::copy_n_if(std::begin(data1), 6, std::begin(data3), std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool is_same = std::equal(std::begin(data2), std::end(data2), std::begin(data3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(copy_if_4_parameter)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};

      int out1[4];
      int out2[2];
      int out3[10];

      int check1[] = {1, 2, 3, 4};
      int check2[] = {1, 2};
      int check3[] = {1, 2, 3, 4, 0, 0, 0, 0, 0, 0};

      int* result;

      // Exact size.
      std::fill(std::begin(out1), std::end(out1), 0);
      result =
        etl::copy_if_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1), std::bind(std::less<int>(), std::placeholders::_1, 5));
      CHECK_EQUAL(std::end(out1), result);
      bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1));
      CHECK(is_same);

      // Destination smaller.
      std::fill(std::begin(out2), std::end(out2), 0);
      result =
        etl::copy_if_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2), std::bind(std::less<int>(), std::placeholders::_1, 5));
      CHECK_EQUAL(std::end(out2), result);
      is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2));
      CHECK(is_same);

      // Destination larger.
      std::fill(std::begin(out3), std::end(out3), 0);
      result =
        etl::copy_if_s(std::begin(data1), std::end(data1), std::begin(out3), std::end(out3), std::bind(std::less<int>(), std::placeholders::_1, 5));
      CHECK_EQUAL(std::begin(out3) + 4, result);
      is_same = std::equal(std::begin(out3), std::end(out3), std::begin(check3));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(binary_find)
    {
      int data1[] = {1, 2, 3, 5, 6, 7, 8};

      // Find the element of value 5.
      int* p = etl::binary_find(std::begin(data1), std::end(data1), 5);
      CHECK_EQUAL(5, *p);

      // Find the element of value 4.
      p = etl::binary_find(std::begin(data1), std::end(data1), 4);
      CHECK_EQUAL(std::end(data1), p);
    }

    //*************************************************************************
    TEST(binary_find_StructDataPredicate_StructDataEquality)
    {
      Data data1[] = {{1, 8}, {2, 7}, {3, 6}, {4, 5}, {5, 4}, {6, 3}, {7, 2}, {8, 1}};
      Data test1   = {4, 5};
      Data test2   = {9, 0};

      // Find the element of value 5.
      Data* p = etl::binary_find(std::begin(data1), std::end(data1), test1, DataPredicate(), DataEquality());
      CHECK_EQUAL(test1, *p);

      // Find the element of value 4.
      p = etl::binary_find(std::begin(data1), std::end(data1), test2, DataPredicate(), DataEquality());
      CHECK_EQUAL(std::end(data1), p);
    }

    //*************************************************************************
    TEST(for_each_if)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};

      struct Sum
      {
        Sum()
          : sum(0)
        {
        }

        Sum& operator()(int i)
        {
          sum += i;

          return *this;
        }

        int sum;
      } accumulator;

      // For each if everything less than 5.
      accumulator = etl::for_each_if(std::begin(data1), std::end(data1), accumulator, std::bind(std::less<int>(), std::placeholders::_1, 5));

      CHECK_EQUAL(10, accumulator.sum);
    }

    //*************************************************************************
    TEST(for_each_n)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int data2[] = {2, 16, 4, 14, 6, 6, 4, 5, 10, 9};

      struct Multiply
      {
        void operator()(int& i)
        {
          i *= 2;
        }
      } multiplier;

      etl::for_each_n(std::begin(data1), 5, multiplier);

      bool are_equal = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(are_equal);
    }

    //*************************************************************************
    TEST(for_each_n_if)
    {
      int data1[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int data2[] = {2, 8, 4, 7, 6, 6, 4, 5, 10, 9};

      struct Multiply
      {
        void operator()(int& i)
        {
          i *= 2;
        }
      } multiplier;

      etl::for_each_n_if(std::begin(data1), 5, multiplier, std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool are_equal = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
      CHECK(are_equal);
    }

    //*************************************************************************
    TEST(transform_4_parameter)
    {
      int input[]   = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {2, 16, 4, 14, 6, 0, 0, 0, 0, 0};

      // Double everything and copy to output.
      etl::transform_s(std::begin(input), std::end(input), std::begin(output), std::begin(output) + (ETL_OR_STD17::size(output) / 2),
                       std::bind(std::multiplies<int>(), std::placeholders::_1, 2));

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);

      std::fill(std::begin(output), std::end(output), 0);

      etl::transform_s(std::begin(input), std::begin(input) + (ETL_OR_STD17::size(input) / 2), std::begin(output), std::end(output),
                       std::bind(std::multiplies<int>(), std::placeholders::_1, 2));

      is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_random_iterator)
    {
      int input[]   = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {2, 16, 4, 14, 6, 12, 8, 0, 0, 0};

      etl::transform_n(std::begin(input), 7, std::begin(output), std::bind(std::multiplies<int>(), std::placeholders::_1, 2));

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_non_random_iterator)
    {
      std::list<int> input     = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int            output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int            compare[] = {2, 16, 4, 14, 6, 12, 8, 0, 0, 0};

      etl::transform_n(std::begin(input), 7, std::begin(output), std::bind(std::multiplies<int>(), std::placeholders::_1, 2));

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_two_ranges_random_iterator)
    {
      int input1[]  = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int input2[]  = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {2, 16, 4, 14, 6, 12, 8, 0, 0, 0};

      etl::transform_n(std::begin(input1), std::begin(input2), 7, std::begin(output), std::plus<int>());

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_two_ranges_non_random_iterator)
    {
      std::list<int> input1    = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      std::list<int> input2    = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int            output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int            compare[] = {2, 16, 4, 14, 6, 12, 8, 0, 0, 0};

      etl::transform_n(std::begin(input1), std::begin(input2), 7, std::begin(output), std::plus<int>());

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_if)
    {
      int input[]   = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {2, 4, 6, 8, 0, 0, 0, 0, 0, 0};

      // Double everything less than 5 and copy to output.
      etl::transform_if(std::begin(input), std::end(input), std::begin(output), std::bind(std::multiplies<int>(), std::placeholders::_1, 2),
                        std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_if_2_input_ranges)
    {
      int input1[]  = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int input2[]  = {8, 7, 6, 5, 4, 10, 9, 3, 2, 1};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {8, 12, 12, 60, 36, 0, 0, 0, 0, 0};

      // Multiply together everything where input1 is less than input2 and copy
      // to output.
      etl::transform_if(std::begin(input1), std::end(input1), std::begin(input2), std::begin(output), std::multiplies<int>(), std::less<int>());

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_if)
    {
      int input[]   = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {2, 4, 6, 0, 0, 0, 0, 0, 0, 0};

      // Double everything less than 5 and copy to output.
      etl::transform_n_if(std::begin(input), 5, std::begin(output), std::bind(std::multiplies<int>(), std::placeholders::_1, 2),
                          std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(transform_n_if_2_input_ranges)
    {
      int input1[]  = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int input2[]  = {8, 7, 6, 5, 4, 10, 9, 3, 2, 1};
      int output[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare[] = {8, 12, 12, 0, 0, 0, 0, 0, 0, 0};

      // Multiply together everything where input1 is less than input2 and copy
      // to output.
      etl::transform_n_if(std::begin(input1), std::begin(input2), 5, std::begin(output), std::multiplies<int>(), std::less<int>());

      bool is_same = std::equal(std::begin(output), std::end(output), std::begin(compare));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(partition_transform)
    {
      int input[]         = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int output_true[]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int output_false[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare_true[]  = {2, 4, 6, 8, 0, 0, 0, 0, 0, 0};
      int compare_false[] = {-16, -14, -12, -10, -20, -18, 0, 0, 0, 0};

      // Multiply everything less than 5 by 2 and copy to output_true.
      // Multiply everything not less than 5 by -2 and copy to output_false.
      etl::partition_transform(std::begin(input), std::end(input), std::begin(output_true), std::begin(output_false),
                               std::bind(std::multiplies<int>(), std::placeholders::_1, 2),
                               std::bind(std::multiplies<int>(), std::placeholders::_1, -2), std::bind(std::less<int>(), std::placeholders::_1, 5));

      bool is_same = std::equal(std::begin(output_true), std::end(output_true), std::begin(compare_true));
      CHECK(is_same);

      is_same = std::equal(std::begin(output_false), std::end(output_false), std::begin(compare_false));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(partition_transform_2_input_ranges)
    {
      int input1[]        = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int input2[]        = {8, 7, 6, 5, 4, 10, 9, 3, 2, 1};
      int output_true[]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int output_false[]  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int compare_true[]  = {8, 12, 12, 60, 36, 0, 0, 0, 0, 0};
      int compare_false[] = {15, 12, 8, 12, 10, 0, 0, 0, 0, 0};

      // If input1 < input2 multiply else add.
      etl::partition_transform(std::begin(input1), std::end(input1), std::begin(input2), std::begin(output_true), std::begin(output_false),
                               std::multiplies<int>(), std::plus<int>(), std::less<int>());

      bool is_same = std::equal(std::begin(output_true), std::end(output_true), std::begin(compare_true));
      CHECK(is_same);

      is_same = std::equal(std::begin(output_false), std::end(output_false), std::begin(compare_false));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(sort_default)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end());
        etl::sort(data2.begin(), data2.end());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(sort_greater)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end(), std::greater<int>());
        etl::sort(data2.begin(), data2.end(), std::greater<int>());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(stable_sort_default)
    {
      std::vector<NDC> initial_data = {NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1)};

      std::vector<NDC> data1(initial_data);
      std::vector<NDC> data2(initial_data);

      std::stable_sort(data1.begin(), data1.end());
      etl::stable_sort(data2.begin(), data2.end());

      bool is_same = std::equal(data1.begin(), data1.end(), data2.begin(), NDC::are_identical);
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(next_permutation)
    {
      std::array<int, 4U> expected = {1, 1, 2, 2};
      std::array<int, 4U> result   = expected;

      for (size_t i = 0U; i < 8U; ++i)
      {
        bool expected_has_next = std::next_permutation(expected.begin(), expected.end());
        bool result_has_next   = etl::next_permutation(result.begin(), result.end());

        CHECK_EQUAL(expected_has_next, result_has_next);
        CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());
      }

      // Check one past the end.
      bool expected_has_next = std::next_permutation(expected.begin(), expected.end());
      bool result_has_next   = etl::next_permutation(result.begin(), result.end());
      CHECK_EQUAL(expected_has_next, result_has_next);
      CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());

      int single_expected[] = {1};
      int single_result[]   = {1};

      expected_has_next = std::next_permutation(std::begin(single_expected), std::end(single_expected));
      result_has_next   = etl::next_permutation(std::begin(single_result), std::end(single_result));

      CHECK_EQUAL(expected_has_next, result_has_next);
      CHECK_ARRAY_EQUAL(single_expected, single_result, 1U);

      // Check for what happens if the beginning and end are the same.
      expected_has_next = std::next_permutation(std::begin(single_expected), std::begin(single_expected));
      result_has_next   = etl::next_permutation(std::begin(single_result), std::begin(single_result));

      CHECK_EQUAL(expected_has_next, result_has_next);
    }

    //*************************************************************************
    TEST(next_permutation_compare)
    {
      std::array<int, 4U> expected = {3, 2, 2, 1};
      std::array<int, 4U> result   = expected;

      for (size_t i = 0U; i < 8U; ++i)
      {
        bool expected_has_next = std::next_permutation(expected.begin(), expected.end(), std::greater<int>());
        bool result_has_next   = etl::next_permutation(result.begin(), result.end(), std::greater<int>());

        CHECK_EQUAL(expected_has_next, result_has_next);
        CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());
      }

      // Check one past the end.
      bool expected_has_next = std::next_permutation(expected.begin(), expected.end(), std::greater<int>());
      bool result_has_next   = etl::next_permutation(result.begin(), result.end(), std::greater<int>());
      CHECK_EQUAL(expected_has_next, result_has_next);
      CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());

      int single_expected[] = {1};
      int single_result[]   = {1};

      // Check for what happens if the beginning and end are the same.
      expected_has_next = std::next_permutation(std::begin(single_expected), std::begin(single_expected), std::greater<int>());
      result_has_next   = etl::next_permutation(std::begin(single_result), std::begin(single_result), std::greater<int>());

      CHECK_EQUAL(expected_has_next, result_has_next);
      CHECK_ARRAY_EQUAL(single_expected, single_result, 1U);
    }

    //*************************************************************************
    TEST(prev_permutation)
    {
      std::array<int, 4U> expected = {2, 2, 1, 1};
      std::array<int, 4U> result   = expected;

      for (size_t i = 0U; i < 8U; ++i)
      {
        bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end());
        bool result_has_prev   = etl::prev_permutation(result.begin(), result.end());

        CHECK_EQUAL(expected_has_prev, result_has_prev);
        CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());
      }

      // Check one past the end.
      bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end());
      bool result_has_prev   = etl::prev_permutation(result.begin(), result.end());
      CHECK_EQUAL(expected_has_prev, result_has_prev);
      CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());

      int single_expected[] = {1};
      int single_result[]   = {1};

      expected_has_prev = std::prev_permutation(std::begin(single_expected), std::end(single_expected));
      result_has_prev   = etl::prev_permutation(std::begin(single_result), std::end(single_result));

      CHECK_EQUAL(expected_has_prev, result_has_prev);
      CHECK_ARRAY_EQUAL(single_expected, single_result, 1U);

      // Check for what happens if the beginning and end are the same.
      expected_has_prev = std::prev_permutation(std::begin(single_expected), std::begin(single_expected));
      result_has_prev   = etl::prev_permutation(std::begin(single_result), std::begin(single_result));

      CHECK_EQUAL(expected_has_prev, result_has_prev);
    }

    //*************************************************************************
    TEST(prev_permutation_compare)
    {
      std::array<int, 4U> expected = {1, 1, 2, 3};
      std::array<int, 4U> result   = expected;

      for (size_t i = 0U; i < 8U; ++i)
      {
        bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end(), std::greater<int>());
        bool result_has_prev   = etl::prev_permutation(result.begin(), result.end(), std::greater<int>());

        CHECK_EQUAL(expected_has_prev, result_has_prev);
        CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());
      }

      // Check one past the end.
      bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end(), std::greater<int>());
      bool result_has_prev   = etl::prev_permutation(result.begin(), result.end(), std::greater<int>());
      CHECK_EQUAL(expected_has_prev, result_has_prev);
      CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size());

      int single_expected[] = {1};
      int single_result[]   = {1};

      // Check for what happens if the beginning and end are the same.
      expected_has_prev = std::prev_permutation(std::begin(single_expected), std::begin(single_expected), std::greater<int>());
      result_has_prev   = etl::prev_permutation(std::begin(single_result), std::begin(single_result), std::greater<int>());

      CHECK_EQUAL(expected_has_prev, result_has_prev);
      CHECK_ARRAY_EQUAL(single_expected, single_result, 1U);
    }

    //*************************************************************************
    TEST(is_permutation_length_mismatch)
    {
      int data1[] = {1, 2, 3};
      int data2[] = {1, 2, 3, 4};

      bool is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(data2), std::end(data2));
      CHECK_FALSE(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(data2), std::end(data2), etl::equal_to<int>());
      CHECK_FALSE(is_permutation);
    }

    //*************************************************************************
    TEST(is_permutation_predicate)
    {
      Data data1[]           = {Data(1, 10), Data(2, 20), Data(2, 30), Data(3, 40)};
      Data permutation[]     = {Data(2, 200), Data(1, 100), Data(3, 300), Data(2, 400)};
      Data not_permutation[] = {Data(2, 200), Data(1, 100), Data(4, 300), Data(2, 400)};

      bool is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), DataEquivalenceByA());
      CHECK_TRUE(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), DataEquivalenceByA());
      CHECK_FALSE(is_permutation);

      is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), std::end(permutation), DataEquivalenceByA());
      CHECK_TRUE(is_permutation);

      is_permutation =
        etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), std::end(not_permutation), DataEquivalenceByA());
      CHECK_FALSE(is_permutation);
    }

    //*************************************************************************
    TEST(shell_sort_default)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end());
        etl::shell_sort(data2.begin(), data2.end());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(shell_sort_greater)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end(), std::greater<int>());
        etl::shell_sort(data2.begin(), data2.end(), std::greater<int>());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(insertion_sort_default)
    {
      std::vector<NDC> initial_data = {NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1)};

      std::vector<NDC> data1(initial_data);
      std::vector<NDC> data2(initial_data);

      std::stable_sort(data1.begin(), data1.end());
      etl::insertion_sort(data2.begin(), data2.end());

      bool is_same = std::equal(data1.begin(), data1.end(), data2.begin(), NDC::are_identical);
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(insertion_sort_greater)
    {
      std::vector<NDC> initial_data = {NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1)};

      std::vector<NDC> data1(initial_data);
      std::vector<NDC> data2(initial_data);

      std::stable_sort(data1.begin(), data1.end(), std::greater<NDC>());
      etl::insertion_sort(data2.begin(), data2.end(), std::greater<NDC>());

      bool is_same = std::equal(data1.begin(), data1.end(), data2.begin(), NDC::are_identical);
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(selection_sort_default_forward_iterators)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int>       data1(data.begin(), data.end());
        std::forward_list<int> data2(data.begin(), data.end());

        std::sort(data1.begin(), data1.end());
        etl::selection_sort(data2.begin(), data2.end());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(selection_sort_default_bidirectional_iterators)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::list<int> data1(data.begin(), data.end());
        std::list<int> data2(data.begin(), data.end());

        data1.sort();
        etl::selection_sort(data2.begin(), data2.end());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(selection_sort_default_random_access_iterators)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end());
        etl::selection_sort(data2.begin(), data2.end());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(selection_sort_greater)
    {
      std::vector<int> data(100, 0);
      std::iota(data.begin(), data.end(), 1);

      for (int i = 0; i < 100; ++i)
      {
        std::shuffle(data.begin(), data.end(), urng);

        std::vector<int> data1 = data;
        std::vector<int> data2 = data;

        std::sort(data1.begin(), data1.end(), std::greater<int>());
        etl::selection_sort(data2.begin(), data2.end(), std::greater<int>());

        bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
        CHECK(is_same);
      }
    }

    //*************************************************************************
    TEST(selection_sort_empty_range)
    {
      // Forward iterators
      std::forward_list<int> fwd_data;
      etl::selection_sort(fwd_data.begin(), fwd_data.end());
      CHECK(fwd_data.empty());

      // Bidirectional iterators
      std::list<int> bidir_data;
      etl::selection_sort(bidir_data.begin(), bidir_data.end());
      CHECK(bidir_data.empty());

      // Random access iterators
      std::vector<int> ra_data;
      etl::selection_sort(ra_data.begin(), ra_data.end());
      CHECK(ra_data.empty());

      // With comparator
      std::forward_list<int> fwd_data2;
      etl::selection_sort(fwd_data2.begin(), fwd_data2.end(), std::greater<int>());
      CHECK(fwd_data2.empty());
    }

    //*************************************************************************
    TEST(heap_sort_default)
    {
      std::vector<NDC> initial_data = {NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1)};

      std::vector<NDC> data1(initial_data);
      std::vector<NDC> data2(initial_data);

      std::sort(data1.begin(), data1.end());
      etl::heap_sort(data2.begin(), data2.end());

      bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(heap_sort_greater)
    {
      std::vector<NDC> initial_data = {NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1)};

      std::vector<NDC> data1(initial_data);
      std::vector<NDC> data2(initial_data);

      std::sort(data1.begin(), data1.end(), std::greater<NDC>());
      etl::heap_sort(data2.begin(), data2.end(), std::greater<NDC>());

      bool is_same = std::equal(data1.begin(), data1.end(), data2.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(multimax)
    {
      CHECK_EQUAL(8, etl::multimax(1, 2, 3, 4, 5, 6, 7, 8));
      CHECK_EQUAL(8, etl::multimax_compare(std::less<int>(), 1, 2, 3, 4, 5, 6, 7, 8));
      CHECK_EQUAL(1, etl::multimax_compare(std::greater<int>(), 1, 2, 3, 4, 5, 6, 7, 8));

      int temp[etl::multimax(1, 2, 3, 4, 5, 6, 7, 8)] = {1, 2, 3, 4, 5, 6, 7, 8};
      (void)temp;
    }

    //*************************************************************************
    TEST(multimax_iter)
    {
      int i[8] = {1, 2, 3, 4, 5, 6, 7, 8};

      CHECK_EQUAL(8, *etl::multimax_iter(&i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
      CHECK_EQUAL(8, *etl::multimax_iter_compare(std::less<int>(), &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
      CHECK_EQUAL(1, *etl::multimax_iter_compare(std::greater<int>(), &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
    }

    //*************************************************************************
    TEST(multimin)
    {
      CHECK_EQUAL(1, etl::multimin(1, 2, 3, 4, 5, 6, 7, 8));
      CHECK_EQUAL(1, etl::multimin_compare(std::less<int>(), 1, 2, 3, 4, 5, 6, 7, 8));
      CHECK_EQUAL(8, etl::multimin_compare(std::greater<int>(), 1, 2, 3, 4, 5, 6, 7, 8));
    }

    //*************************************************************************
    TEST(multimin_iter)
    {
      int i[8] = {1, 2, 3, 4, 5, 6, 7, 8};

      CHECK_EQUAL(1, *etl::multimin_iter(&i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
      CHECK_EQUAL(1, *etl::multimin_iter_compare(std::less<int>(), &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
      CHECK_EQUAL(8, *etl::multimin_iter_compare(std::greater<int>(), &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7]));
    }

    //*************************************************************************
    TEST(replace)
    {
      int data[]     = {1, 8, 2, 7, 2, 6, 2, 2, 10, 9};
      int expected[] = {1, 8, 0, 7, 0, 6, 0, 0, 10, 9};

      // Replace 2 with 0
      etl::replace(std::begin(data), std::end(data), 2, 0);

      bool is_same = std::equal(std::begin(data), std::end(data), std::begin(expected));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(replace_if)
    {
      int data[]     = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};
      int expected[] = {0, 8, 0, 7, 0, 6, 0, 0, 10, 9};

      // Replace <=5 with 0
      etl::replace_if(std::begin(data), std::end(data), std::bind(std::less_equal<int>(), std::placeholders::_1, 5), 0);

      bool is_same = std::equal(std::begin(data), std::end(data), std::begin(expected));
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(for_each)
    {
      int data[] = {1, 8, 2, 7, 3, 6, 4, 5, 10, 9};

      struct Sum
      {
        void operator()(int i)
        {
          value += i;
        }

        Sum()
          : value(0)
        {
        }

        int value;
      };

      Sum sum;
      sum = etl::for_each(std::begin(data), std::end(data), sum);
      CHECK_EQUAL(std::accumulate(std::begin(data), std::end(data), 0), sum.value);
    }

    //*************************************************************************
    TEST(remove)
    {
      std::array<int, 10> data     = {1, 8, 2, 7, 7, 7, 4, 5, 10, 9};
      std::array<int, 7>  expected = {1, 8, 2, 4, 5, 10, 9};

      etl::remove(data.begin(), data.end(), 7);

      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(remove_if)
    {
      std::array<int, 10> data     = {1, 8, 2, 7, 7, 7, 4, 5, 10, 9};
      std::array<int, 4>  expected = {1, 2, 4, 5};

      etl::remove_if(data.begin(), data.end(), [](int value) { return value >= 7; });

      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique)
    {
      std::array<int, 10> data     = {1, 1, 2, 3, 3, 3, 4, 4, 5, 5};
      std::array<int, 5>  expected = {1, 2, 3, 4, 5};

      auto end = etl::unique(data.begin(), data.end());

      CHECK_EQUAL(5, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_empty_range)
    {
      std::array<int, 0> data = {};

      auto end = etl::unique(data.begin(), data.end());

      CHECK(end == data.end());
    }

    //*************************************************************************
    TEST(unique_single_element)
    {
      std::array<int, 1> data     = {42};
      std::array<int, 1> expected = {42};

      auto end = etl::unique(data.begin(), data.end());

      CHECK_EQUAL(1, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_no_duplicates)
    {
      std::array<int, 5> data     = {1, 2, 3, 4, 5};
      std::array<int, 5> expected = {1, 2, 3, 4, 5};

      auto end = etl::unique(data.begin(), data.end());

      CHECK_EQUAL(5, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_all_same)
    {
      std::array<int, 5> data     = {7, 7, 7, 7, 7};
      std::array<int, 1> expected = {7};

      auto end = etl::unique(data.begin(), data.end());

      CHECK_EQUAL(1, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_with_predicate)
    {
      std::array<int, 10> data     = {1, 1, 2, 3, 3, 3, 4, 4, 5, 5};
      std::array<int, 5>  expected = {1, 2, 3, 4, 5};

      auto end = etl::unique(data.begin(), data.end(), std::equal_to<int>());

      CHECK_EQUAL(5, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_with_predicate_custom)
    {
      // Group elements that are close to each other (differ by less than 3)
      std::array<int, 8> data     = {1, 2, 3, 7, 8, 9, 20, 21};
      std::array<int, 3> expected = {1, 7, 20};

      auto end = etl::unique(data.begin(), data.end(), [](int a, int b) { return (b - a) < 3; });

      CHECK_EQUAL(3, std::distance(data.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_matches_std)
    {
      std::array<int, 12> data1 = {1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5};
      std::array<int, 12> data2 = data1;

      auto std_end = std::unique(data1.begin(), data1.end());
      auto etl_end = etl::unique(data2.begin(), data2.end());

      size_t std_size = static_cast<size_t>(std::distance(data1.begin(), std_end));
      size_t etl_size = static_cast<size_t>(std::distance(data2.begin(), etl_end));

      CHECK_EQUAL(std_size, etl_size);
      bool is_same = std::equal(data1.begin(), std_end, data2.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_with_predicate_matches_std)
    {
      std::array<int, 12> data1 = {1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5};
      std::array<int, 12> data2 = data1;

      auto std_end = std::unique(data1.begin(), data1.end(), std::equal_to<int>());
      auto etl_end = etl::unique(data2.begin(), data2.end(), std::equal_to<int>());

      size_t std_size = static_cast<size_t>(std::distance(data1.begin(), std_end));
      size_t etl_size = static_cast<size_t>(std::distance(data2.begin(), etl_end));

      CHECK_EQUAL(std_size, etl_size);
      bool is_same = std::equal(data1.begin(), std_end, data2.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy)
    {
      std::array<int, 10> data     = {1, 1, 2, 3, 3, 3, 4, 4, 5, 5};
      std::array<int, 5>  expected = {1, 2, 3, 4, 5};
      std::array<int, 10> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin());

      CHECK_EQUAL(5, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_empty_range)
    {
      std::array<int, 0> data   = {};
      std::array<int, 1> result = {99};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin());

      CHECK(end == result.begin());
      CHECK_EQUAL(99, result[0]); // output unchanged
    }

    //*************************************************************************
    TEST(unique_copy_single_element)
    {
      std::array<int, 1> data     = {42};
      std::array<int, 1> expected = {42};
      std::array<int, 1> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin());

      CHECK_EQUAL(1, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_no_duplicates)
    {
      std::array<int, 5> data     = {1, 2, 3, 4, 5};
      std::array<int, 5> expected = {1, 2, 3, 4, 5};
      std::array<int, 5> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin());

      CHECK_EQUAL(5, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_all_same)
    {
      std::array<int, 5> data     = {7, 7, 7, 7, 7};
      std::array<int, 1> expected = {7};
      std::array<int, 5> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin());

      CHECK_EQUAL(1, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_source_unchanged)
    {
      std::array<int, 10> data     = {1, 1, 2, 3, 3, 3, 4, 4, 5, 5};
      std::array<int, 10> original = data;
      std::array<int, 10> result   = {};

      etl::unique_copy(data.begin(), data.end(), result.begin());

      bool is_same = std::equal(original.begin(), original.end(), data.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_with_predicate)
    {
      std::array<int, 10> data     = {1, 1, 2, 3, 3, 3, 4, 4, 5, 5};
      std::array<int, 5>  expected = {1, 2, 3, 4, 5};
      std::array<int, 10> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), std::equal_to<int>());

      CHECK_EQUAL(5, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_with_predicate_custom)
    {
      // Group elements that are close to each other (differ by less than 3)
      std::array<int, 8> data     = {1, 2, 3, 7, 8, 9, 20, 21};
      std::array<int, 3> expected = {1, 7, 20};
      std::array<int, 8> result   = {};

      auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), [](int a, int b) { return (b - a) < 3; });

      CHECK_EQUAL(3, std::distance(result.begin(), end));
      bool is_same = std::equal(expected.begin(), expected.end(), result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_matches_std)
    {
      std::array<int, 12> data       = {1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5};
      std::array<int, 12> std_result = {};
      std::array<int, 12> etl_result = {};

      auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin());
      auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin());

      size_t std_size = static_cast<size_t>(std::distance(std_result.begin(), std_end));
      size_t etl_size = static_cast<size_t>(std::distance(etl_result.begin(), etl_end));

      CHECK_EQUAL(std_size, etl_size);
      bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    TEST(unique_copy_with_predicate_matches_std)
    {
      std::array<int, 12> data       = {1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5};
      std::array<int, 12> std_result = {};
      std::array<int, 12> etl_result = {};

      auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin(), std::equal_to<int>());
      auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin(), std::equal_to<int>());

      size_t std_size = static_cast<size_t>(std::distance(std_result.begin(), std_end));
      size_t etl_size = static_cast<size_t>(std::distance(etl_result.begin(), etl_end));

      CHECK_EQUAL(std_size, etl_size);
      bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin());
      CHECK(is_same);
    }

    //*************************************************************************
    struct generator
    {
      generator(int value_)
        : value(value_)
      {
      }

      int operator()()
      {
        return value++;
      }

      int value;
    };

    TEST(generate)
    {
      std::array<int, 10> expected = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
      std::array<int, 10> actual;

      etl::generate(actual.begin(), actual.end(), generator(2));

      CHECK_ARRAY_EQUAL(expected.data(), actual.data(), expected.size());
    }

    //*************************************************************************
    TEST(partition_forward_iterator_container)
    {
      // 40,320 permutations.
      std::array<int, 8> origin = {0, 1, 2, 3, 4, 5, 6, 7};

      std::forward_list<int> compare(origin.begin(), origin.end());
      std::forward_list<int> data(origin.begin(), origin.end());

      bool complete = false;

      while (!complete)
      {
        auto pivot1 = std::partition(compare.begin(), compare.end(), [](int i) { return etl::is_even(i); });
        auto pivot2 = etl::partition(data.begin(), data.end(), [](int i) { return etl::is_even(i); });

        auto distance1 = std::distance(compare.begin(), pivot1);
        auto distance2 = std::distance(data.begin(), pivot2);

        CHECK_EQUAL(*pivot1, *pivot2);
        CHECK_EQUAL(distance1, distance2);

        for (auto itr = compare.begin(); itr != pivot1; ++itr)
        {
          CHECK_TRUE((etl::is_even(*itr)));
        }

        for (auto itr = pivot1; itr != compare.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(*itr)));
        }

        complete = !std::next_permutation(origin.begin(), origin.end());

        compare.assign(origin.begin(), origin.end());
        data.assign(origin.begin(), origin.end());
      }
    }

    //*************************************************************************
    TEST(partition_bidirectional_iterator_container)
    {
      // 40,320 permutations.
      std::array<int, 8> initial = {0, 1, 2, 3, 4, 5, 6, 7};

      std::array<int, 8> compare = initial;
      std::array<int, 8> data    = initial;

      bool complete = false;

      while (!complete)
      {
        auto pivot1 = std::partition(compare.begin(), compare.end(), [](int i) { return etl::is_even(i); });
        auto pivot2 = etl::partition(data.begin(), data.end(), [](int i) { return etl::is_even(i); });

        auto distance1 = std::distance(compare.begin(), pivot1);
        auto distance2 = std::distance(data.begin(), pivot2);

        CHECK_EQUAL(*pivot1, *pivot2);
        CHECK_EQUAL(distance1, distance2);

        for (auto itr = compare.begin(); itr != pivot1; ++itr)
        {
          CHECK_TRUE((etl::is_even(*itr)));
        }

        for (auto itr = pivot1; itr != compare.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(*itr)));
        }

        complete = !std::next_permutation(initial.begin(), initial.end());

        compare = initial;
        data    = initial;
      }
    }

    //*************************************************************************
    TEST(stable_partition_in_place_bidirectional_iterator_container)
    {
      // 40,320 permutations.
      std::array<int, 8> initial_data = {0, 1, 2, 3, 4, 5, 6, 7};

      std::array<int, 8> std_data = initial_data;
      std::array<int, 8> etl_data = initial_data;

      bool complete = false;

      while (!complete)
      {
        auto std_pivot = std::stable_partition(std_data.begin(), std_data.end(), [](int i) { return etl::is_even(i); });
        auto etl_pivot = etl::stable_partition(etl_data.begin(), etl_data.end(), [](int i) { return etl::is_even(i); });

        auto std_distance = std::distance(std_data.begin(), std_pivot);
        auto etl_distance = std::distance(etl_data.begin(), etl_pivot);

        CHECK_EQUAL(*std_pivot, *etl_pivot);
        CHECK_EQUAL(std_distance, etl_distance);

        for (auto itr = std_data.begin(); itr != std_pivot; ++itr)
        {
          CHECK_TRUE((etl::is_even(*itr)));
        }

        for (auto itr = std_pivot; itr != std_data.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(*itr)));
        }

        complete = !std::next_permutation(initial_data.begin(), initial_data.end());

        std_data = initial_data;
        etl_data = initial_data;
      }
    }

    //*************************************************************************
    TEST(stable_partition_in_place_bidirectional_iterator_container_for_move_only_elements)
    {
      // 40,320 permutations.
      std::array<TestDataM<int>, 8> initial_data = {TestDataM<int>(0), TestDataM<int>(1), TestDataM<int>(2), TestDataM<int>(3),
                                                    TestDataM<int>(4), TestDataM<int>(5), TestDataM<int>(6), TestDataM<int>(7)};

      std::array<TestDataM<int>, 8> std_data = std::move(initial_data);
      std::array<TestDataM<int>, 8> etl_data = std::move(initial_data);

      bool complete = false;

      while (!complete)
      {
        auto std_pivot = std::stable_partition(std_data.begin(), std_data.end(), [](const TestDataM<int>& t) { return etl::is_even(t.value); });
        auto etl_pivot = etl::stable_partition(etl_data.begin(), etl_data.end(), [](const TestDataM<int>& t) { return etl::is_even(t.value); });

        auto std_distance = std::distance(std_data.begin(), std_pivot);
        auto etl_distance = std::distance(etl_data.begin(), etl_pivot);

        CHECK_EQUAL(*std_pivot, *etl_pivot);
        CHECK_EQUAL(std_distance, etl_distance);

        for (auto itr = std_data.begin(); itr != std_pivot; ++itr)
        {
          CHECK_TRUE((etl::is_even(itr->value)));
        }

        for (auto itr = std_pivot; itr != std_data.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(itr->value)));
        }

        complete = !std::next_permutation(initial_data.begin(), initial_data.end());

        std_data = std::move(initial_data);
        etl_data = std::move(initial_data);
      }
    }

    //*************************************************************************
    TEST(stable_partition_external_buffer_bidirectional_iterator_container)
    {
      // 40,320 permutations.
      std::array<int, 8> initial_data = {0, 1, 2, 3, 4, 5, 6, 7};

      std::array<int, 8> buffer = {0, 0, 0, 0, 0, 0, 0, 0};

      std::array<int, 8> std_data = initial_data;
      std::array<int, 8> etl_data = initial_data;

      bool complete = false;

      while (!complete)
      {
        auto std_pivot = std::stable_partition(std_data.begin(), std_data.end(), [](int i) { return etl::is_even(i); });
        auto etl_pivot = etl::stable_partition(etl_data.begin(), etl_data.end(), buffer.begin(), buffer.end(), [](int i) { return etl::is_even(i); });

        auto std_distance = std::distance(std_data.begin(), std_pivot);
        auto etl_distance = std::distance(etl_data.begin(), etl_pivot);

        CHECK_EQUAL(*std_pivot, *etl_pivot);
        CHECK_EQUAL(std_distance, etl_distance);

        for (auto itr = std_data.begin(); itr != std_pivot; ++itr)
        {
          CHECK_TRUE((etl::is_even(*itr)));
        }

        for (auto itr = std_pivot; itr != std_data.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(*itr)));
        }

        complete = !std::next_permutation(initial_data.begin(), initial_data.end());

        std_data = initial_data;
        etl_data = initial_data;
      }
    }

    //*************************************************************************
    TEST(stable_partition_external_buffer_bidirectional_iterator_container_for_move_only_elements)
    {
      // 40,320 permutations.
      std::array<TestDataM<int>, 8> initial_data = {TestDataM<int>(0), TestDataM<int>(1), TestDataM<int>(2), TestDataM<int>(3),
                                                    TestDataM<int>(4), TestDataM<int>(5), TestDataM<int>(6), TestDataM<int>(7)};

      std::array<TestDataM<int>, 8> buffer = {TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0),
                                              TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0), TestDataM<int>(0)};

      std::array<TestDataM<int>, 8> std_data = std::move(initial_data);
      std::array<TestDataM<int>, 8> etl_data = std::move(initial_data);

      bool complete = false;

      while (!complete)
      {
        auto std_pivot = std::stable_partition(std_data.begin(), std_data.end(), [](const TestDataM<int>& t) { return etl::is_even(t.value); });
        auto etl_pivot = etl::stable_partition(etl_data.begin(), etl_data.end(), buffer.begin(), buffer.end(),
                                               [](const TestDataM<int>& t) { return etl::is_even(t.value); });

        auto std_distance = std::distance(std_data.begin(), std_pivot);
        auto etl_distance = std::distance(etl_data.begin(), etl_pivot);

        CHECK_EQUAL(std_pivot->value, etl_pivot->value);
        CHECK_EQUAL(std_distance, etl_distance);

        for (auto itr = std_data.begin(); itr != std_pivot; ++itr)
        {
          CHECK_TRUE((etl::is_even(itr->value)));
        }

        for (auto itr = std_pivot; itr != std_data.end(); ++itr)
        {
          CHECK_FALSE((etl::is_even(itr->value)));
        }

        complete = !std::next_permutation(initial_data.begin(), initial_data.end());

        std_data = std::move(initial_data);
        etl_data = std::move(initial_data);
      }
    }

    //*************************************************************************
    TEST(stable_partition_external_buffer_too_small)
    {
      int buffer[7];
      int etl_data[8];

      CHECK_THROW((etl::stable_partition(std::begin(etl_data), std::end(etl_data), std::begin(buffer), std::end(buffer),
                                         [](int i) { return etl::is_even(i); })),
                  etl::stable_partition_buffer_too_small);
    }

    //*************************************************************************
    TEST(nth_element_with_default_less_than_comparison)
    {
      // 40,320 permutations.
      std::array<int, 8> initial = {0, 1, 2, 3, 4, 5, 6, 7};

      std::array<int, 8> compare = initial;
      std::array<int, 8> data    = initial;

      bool complete = false;

      // For each nth position of each permutation.
      while (!complete)
      {
        // Try each nth position.
        for (size_t i = 0; i < initial.size(); ++i)
        {
          std::sort(compare.begin(), compare.end());
          etl::nth_element(data.begin(), data.begin() + i, data.end());

          CHECK_EQUAL(compare[i], data[i]);
        }

        complete = !std::next_permutation(initial.begin(), initial.end());

        compare = initial;
        data    = initial;
      }
    }

#if (ETL_USING_CPP20 && ETL_USING_STL) || (ETL_USING_CPP14 && ETL_NOT_USING_STL && !defined(ETL_IN_UNIT_TEST))
    //*************************************************************************
    constexpr int MakeNth(int nth_index)
    {
      std::array<int, 8> data = {5, 1, 3, 7, 6, 2, 4, 0};

      etl::nth_element(data.begin(), data.begin() + nth_index, data.end());

      return data[static_cast<size_t>(nth_index)];
    }

    TEST(constexpr_nth_element_with_default_less_than_comparison)
    {
      std::array<int, 8> compare = {0, 1, 2, 3, 4, 5, 6, 7};

      constexpr int nth = MakeNth(3);

      CHECK_EQUAL(compare[3], nth);
    }
#endif

    //*************************************************************************
    TEST(nth_element_with_custom_comparison)
    {
      // 40,320 permutations.
      std::array<int, 8> initial = {0, 1, 2, 3, 4, 5, 6, 7};

      std::array<int, 8> compare = initial;
      std::array<int, 8> data    = initial;

      bool complete = false;

      // For each nth position of each permutation.
      while (!complete)
      {
        // Try each nth position.
        for (size_t i = 0; i < initial.size(); ++i)
        {
          std::sort(compare.begin(), compare.end(), std::greater<int>());
          etl::nth_element(data.begin(), data.begin() + i, data.end(), std::greater<int>());

          CHECK_EQUAL(compare[i], data[i]);
        }

        complete = !std::next_permutation(initial.begin(), initial.end());

        compare = initial;
        data    = initial;
      }
    }

    //*************************************************************************
    TEST(accumulate_default)
    {
      int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      int expected = std::accumulate(std::begin(data), std::end(data), 0);
      int result   = etl::accumulate(std::begin(data), std::end(data), 0);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_with_initial_value)
    {
      int data[] = {1, 2, 3, 4, 5};

      int expected = std::accumulate(std::begin(data), std::end(data), 100);
      int result   = etl::accumulate(std::begin(data), std::end(data), 100);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_custom_operation)
    {
      int data[] = {1, 2, 3, 4, 5};

      int expected = std::accumulate(std::begin(data), std::end(data), 1, std::multiplies<int>());
      int result   = etl::accumulate(std::begin(data), std::end(data), 1, std::multiplies<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_empty_range)
    {
      int data[] = {1};

      int expected = std::accumulate(std::begin(data), std::begin(data), 42);
      int result   = etl::accumulate(std::begin(data), std::begin(data), 42);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_single_element)
    {
      int data[] = {7};

      int expected = std::accumulate(std::begin(data), std::end(data), 0);
      int result   = etl::accumulate(std::begin(data), std::end(data), 0);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_negative_values)
    {
      int data[] = {-3, -2, -1, 0, 1, 2, 3};

      int expected = std::accumulate(std::begin(data), std::end(data), 0);
      int result   = etl::accumulate(std::begin(data), std::end(data), 0);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_custom_operation_subtraction)
    {
      int data[] = {1, 2, 3, 4, 5};

      int expected = std::accumulate(std::begin(data), std::end(data), 100, std::minus<int>());
      int result   = etl::accumulate(std::begin(data), std::end(data), 100, std::minus<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_non_random_iterator)
    {
      List data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      int expected = std::accumulate(data.begin(), data.end(), 0);
      int result   = etl::accumulate(data.begin(), data.end(), 0);

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_non_random_iterator_custom_operation)
    {
      List data = {1, 2, 3, 4, 5};

      int expected = std::accumulate(data.begin(), data.end(), 1, std::multiplies<int>());
      int result   = etl::accumulate(data.begin(), data.end(), 1, std::multiplies<int>());

      CHECK_EQUAL(expected, result);
    }

    //*************************************************************************
    TEST(accumulate_double)
    {
      double data[] = {1.5, 2.5, 3.5, 4.5, 5.5};

      double expected = std::accumulate(std::begin(data), std::end(data), 0.0);
      double result   = etl::accumulate(std::begin(data), std::end(data), 0.0);

      CHECK_CLOSE(expected, result, 1e-10);
    }

    //*************************************************************************
    TEST(clamp_run_time)
    {
      CHECK_EQUAL(5, etl::clamp(5, 0, 10));
      CHECK_EQUAL(0, etl::clamp(-5, 0, 10));
      CHECK_EQUAL(10, etl::clamp(15, 0, 10));
    }

    //*************************************************************************
    TEST(clamp_compile_time)
    {
      CHECK_EQUAL(5, (etl::clamp<int, 0, 10>(5)));
      CHECK_EQUAL(0, (etl::clamp<int, 0, 10>(-5)));
      CHECK_EQUAL(10, (etl::clamp<int, 0, 10>(15)));
    }

    //*************************************************************************
    TEST(clamp_constexpr)
    {
      constexpr int result1 = etl::clamp(5, 0, 10);
      constexpr int result2 = etl::clamp(-5, 0, 10);
      constexpr int result3 = etl::clamp(15, 0, 10);

      constexpr int result4 = etl::clamp<int, 0, 10>(5);
      constexpr int result5 = etl::clamp<int, 0, 10>(-5);
      constexpr int result6 = etl::clamp<int, 0, 10>(15);

      CHECK_EQUAL(5, result1);
      CHECK_EQUAL(0, result2);
      CHECK_EQUAL(10, result3);

      CHECK_EQUAL(5, result4);
      CHECK_EQUAL(0, result5);
      CHECK_EQUAL(10, result6);
    }

    //*************************************************************************
    TEST(merge_default_comparator)
    {
      int input1[] = {1, 3, 5, 7, 9};
      int input2[] = {2, 4, 6, 8, 10};
      int output[10];
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      int* result = etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 10);
    }

    //*************************************************************************
    TEST(merge_custom_comparator)
    {
      int input1[] = {9, 7, 5, 3, 1};
      int input2[] = {10, 8, 6, 4, 2};
      int output[10];
      int expected[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};

      int* result = etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output), Greater());

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 10);
    }

    //*************************************************************************
    TEST(merge_first_range_empty)
    {
      int input1[] = {0}; // dummy, won't be used
      int input2[] = {1, 2, 3};
      int output[3];
      int expected[] = {1, 2, 3};

      int* result = etl::merge(input1, input1, // empty range
                               std::begin(input2), std::end(input2), std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 3);
    }

    //*************************************************************************
    TEST(merge_second_range_empty)
    {
      int input1[] = {1, 2, 3};
      int input2[] = {0}; // dummy, won't be used
      int output[3];
      int expected[] = {1, 2, 3};

      int* result = etl::merge(std::begin(input1), std::end(input1), input2,
                               input2, // empty range
                               std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 3);
    }

    //*************************************************************************
    TEST(merge_both_ranges_empty)
    {
      int input1[] = {0};
      int output[] = {99};

      int* result = etl::merge(input1, input1, input1, input1, std::begin(output));

      CHECK_EQUAL(std::begin(output), result);
      CHECK_EQUAL(99, output[0]); // output should be unchanged
    }

    //*************************************************************************
    TEST(merge_with_duplicates)
    {
      int input1[] = {1, 3, 3, 5};
      int input2[] = {2, 3, 4, 5};
      int output[8];
      int expected[] = {1, 2, 3, 3, 3, 4, 5, 5};

      int* result = etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 8);
    }

    //*************************************************************************
    TEST(merge_different_sizes)
    {
      int input1[] = {1, 5};
      int input2[] = {2, 3, 4, 6, 7, 8};
      int output[8];
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8};

      int* result = etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 8);
    }

    //*************************************************************************
    TEST(merge_single_elements)
    {
      int input1[] = {1};
      int input2[] = {2};
      int output[2];
      int expected[] = {1, 2};

      int* result = etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output));

      CHECK_EQUAL(std::end(output), result);
      CHECK_ARRAY_EQUAL(expected, output, 2);
    }

    //*************************************************************************
    TEST(merge_with_list_iterators)
    {
      std::list<int>   input1 = {1, 3, 5, 7};
      std::list<int>   input2 = {2, 4, 6, 8};
      std::vector<int> output(8);
      std::vector<int> expected = {1, 2, 3, 4, 5, 6, 7, 8};

      std::vector<int>::iterator result = etl::merge(input1.begin(), input1.end(), input2.begin(), input2.end(), output.begin());

      CHECK(output.end() == result);
      CHECK_ARRAY_EQUAL(expected.data(), output.data(), 8);
    }

    //*************************************************************************
    TEST(merge_matches_std)
    {
      int input1[] = {1, 4, 7, 8, 10};
      int input2[] = {2, 3, 5, 6, 9};
      int etl_output[10];
      int std_output[10];

      etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(etl_output));

      std::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(std_output));

      CHECK_ARRAY_EQUAL(std_output, etl_output, 10);
    }

    //*************************************************************************
    TEST(merge_matches_std_with_comparator)
    {
      int input1[] = {10, 8, 7, 4, 1};
      int input2[] = {9, 6, 5, 3, 2};
      int etl_output[10];
      int std_output[10];

      etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(etl_output), Greater());

      std::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(std_output), Greater());

      CHECK_ARRAY_EQUAL(std_output, etl_output, 10);
    }

    //*************************************************************************
    TEST(merge_stability)
    {
      // Test that merge is stable: equivalent elements from the first range
      // come before those from the second range.
      Data input1[] = {Data(1, 1), Data(3, 1), Data(5, 1)};
      Data input2[] = {Data(1, 2), Data(3, 2), Data(5, 2)};
      Data output[6];

      etl::merge(std::begin(input1), std::end(input1), std::begin(input2), std::end(input2), std::begin(output), DataPredicate());

      // Elements from input1 (b==1) should come before elements from input2
      // (b==2) for equivalent keys.
      CHECK_EQUAL(1, output[0].a);
      CHECK_EQUAL(1, output[0].b); // from input1
      CHECK_EQUAL(1, output[1].a);
      CHECK_EQUAL(2, output[1].b); // from input2
      CHECK_EQUAL(3, output[2].a);
      CHECK_EQUAL(1, output[2].b); // from input1
      CHECK_EQUAL(3, output[3].a);
      CHECK_EQUAL(2, output[3].b); // from input2
      CHECK_EQUAL(5, output[4].a);
      CHECK_EQUAL(1, output[4].b); // from input1
      CHECK_EQUAL(5, output[5].a);
      CHECK_EQUAL(2, output[5].b); // from input2
    }

    //*************************************************************************
    TEST(inplace_merge_default_comparator)
    {
      int data[]     = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_custom_comparator)
    {
      int data[]     = {9, 7, 5, 3, 1, 10, 8, 6, 4, 2};
      int expected[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};

      etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data), Greater());

      CHECK_ARRAY_EQUAL(expected, data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_first_range_empty)
    {
      int data[]     = {1, 2, 3, 4, 5};
      int expected[] = {1, 2, 3, 4, 5};

      etl::inplace_merge(std::begin(data), std::begin(data), std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 5);
    }

    //*************************************************************************
    TEST(inplace_merge_second_range_empty)
    {
      int data[]     = {1, 2, 3, 4, 5};
      int expected[] = {1, 2, 3, 4, 5};

      etl::inplace_merge(std::begin(data), std::end(data), std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 5);
    }

    //*************************************************************************
    TEST(inplace_merge_both_ranges_empty)
    {
      int data[] = {99};

      etl::inplace_merge(data, data, data); // empty range

      CHECK_EQUAL(99, data[0]); // unchanged
    }

    //*************************************************************************
    TEST(inplace_merge_with_duplicates)
    {
      int data[]     = {1, 3, 3, 5, 2, 3, 4, 5};
      int expected[] = {1, 2, 3, 3, 3, 4, 5, 5};

      etl::inplace_merge(std::begin(data), std::begin(data) + 4, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 8);
    }

    //*************************************************************************
    TEST(inplace_merge_different_sizes)
    {
      int data[]     = {1, 5, 2, 3, 4, 6, 7, 8};
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8};

      etl::inplace_merge(std::begin(data), std::begin(data) + 2, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 8);
    }

    //*************************************************************************
    TEST(inplace_merge_single_elements)
    {
      int data[]     = {2, 1};
      int expected[] = {1, 2};

      etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 2);
    }

    //*************************************************************************
    TEST(inplace_merge_single_element_halves_already_sorted)
    {
      int data[]     = {1, 2};
      int expected[] = {1, 2};

      etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 2);
    }

    //*************************************************************************
    TEST(inplace_merge_already_sorted)
    {
      int data[]     = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_reverse_halves)
    {
      // Second half all less than first half
      int data[]     = {6, 7, 8, 9, 10, 1, 2, 3, 4, 5};
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_with_list_iterators)
    {
      std::vector<int> data     = {1, 3, 5, 7, 2, 4, 6, 8};
      std::vector<int> expected = {1, 2, 3, 4, 5, 6, 7, 8};

      etl::inplace_merge(data.begin(), data.begin() + 4, data.end());

      CHECK(expected == data);
    }

    //*************************************************************************
    TEST(inplace_merge_matches_std)
    {
      int etl_data[] = {1, 4, 7, 8, 10, 2, 3, 5, 6, 9};
      int std_data[] = {1, 4, 7, 8, 10, 2, 3, 5, 6, 9};

      etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data));
      std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data));

      CHECK_ARRAY_EQUAL(std_data, etl_data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_matches_std_with_comparator)
    {
      int etl_data[] = {10, 8, 7, 4, 1, 9, 6, 5, 3, 2};
      int std_data[] = {10, 8, 7, 4, 1, 9, 6, 5, 3, 2};

      etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data), Greater());
      std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data), Greater());

      CHECK_ARRAY_EQUAL(std_data, etl_data, 10);
    }

    //*************************************************************************
    TEST(inplace_merge_stability)
    {
      // Test that inplace_merge is stable: equivalent elements from the first
      // range come before those from the second range.
      Data data[] = {Data(1, 1), Data(3, 1), Data(5, 1), Data(1, 2), Data(3, 2), Data(5, 2)};

      etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data), DataPredicate());

      // Elements from first half (b==1) should come before elements from second
      // half (b==2) for equivalent keys.
      CHECK_EQUAL(1, data[0].a);
      CHECK_EQUAL(1, data[0].b); // from first half
      CHECK_EQUAL(1, data[1].a);
      CHECK_EQUAL(2, data[1].b); // from second half
      CHECK_EQUAL(3, data[2].a);
      CHECK_EQUAL(1, data[2].b); // from first half
      CHECK_EQUAL(3, data[3].a);
      CHECK_EQUAL(2, data[3].b); // from second half
      CHECK_EQUAL(5, data[4].a);
      CHECK_EQUAL(1, data[4].b); // from first half
      CHECK_EQUAL(5, data[5].a);
      CHECK_EQUAL(2, data[5].b); // from second half
    }

    //*************************************************************************
    TEST(inplace_merge_single_element_first_half)
    {
      int data[]     = {5, 1, 2, 3, 4};
      int expected[] = {1, 2, 3, 4, 5};

      etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 5);
    }

    //*************************************************************************
    TEST(inplace_merge_single_element_second_half)
    {
      int data[]     = {1, 2, 3, 4, 5, 3};
      int expected[] = {1, 2, 3, 3, 4, 5};

      etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 6);
    }

    //*************************************************************************
    TEST(inplace_merge_all_equal)
    {
      int data[]     = {5, 5, 5, 5, 5, 5};
      int expected[] = {5, 5, 5, 5, 5, 5};

      etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 6);
    }

    //*************************************************************************
    TEST(inplace_merge_interleaved)
    {
      int data[]     = {1, 3, 5, 7, 9, 11, 2, 4, 6, 8, 10, 12};
      int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

      etl::inplace_merge(std::begin(data), std::begin(data) + 6, std::end(data));

      CHECK_ARRAY_EQUAL(expected, data, 12);
    }
  }
} // namespace
