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

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

Copyright(c) 2017 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/debug_count.h"
#include "etl/endianness.h"
#include "etl/list.h"
#include "etl/memory.h"
#include "etl/span.h"

#include "data.h"

#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <memory>
#include <numeric>
#include <stdint.h>
#include <string>
#include <type_traits>
#include <vector>

//***************************************************************************
/// A non-trivially-relocatable type that tracks moves and destructions.
/// Used to exercise the manual move-and-destroy path of etl::relocate.
//***************************************************************************
struct relocatable_t
{
  int  value;
  bool was_moved_into; ///< true when this object was constructed via move

  static int destructor_count;

  static void reset_counts()
  {
    destructor_count = 0;
  }

  explicit relocatable_t(int v = 0)
    : value(v)
    , was_moved_into(false)
  {
  }

  relocatable_t(relocatable_t&& other) ETL_NOEXCEPT
    : value(other.value)
    , was_moved_into(true)
  {
    other.value = -1; // mark source as moved-from
  }

  ~relocatable_t()
  {
    ++destructor_count;
  }

  // Non-copyable to make the intent clear.
  relocatable_t(const relocatable_t&)            = delete;
  relocatable_t& operator=(const relocatable_t&) = delete;
  relocatable_t& operator=(relocatable_t&&)      = delete;
};

int relocatable_t::destructor_count = 0;

// In configurations where etl::is_nothrow_relocatable is a class template
// (non-STL builds), we must provide an explicit specialisation so that
// etl::relocate is enabled for relocatable_t.  When the STL is available the
// trait is a type alias that already evaluates to true for types with a
// nothrow move constructor and a nothrow destructor, so no specialisation is
// needed (or even possible).
#if !(ETL_USING_STL && ETL_USING_CPP11)
namespace etl
{
  template <>
  struct is_nothrow_relocatable<relocatable_t> : public etl::true_type
  {
  };
} // namespace etl
#endif

namespace
{
  typedef std::string    non_trivial_t;
  typedef uint32_t       trivial_t;
  typedef TestDataM<int> moveable_t;

  const size_t SIZE = 10UL;

  std::array<non_trivial_t, SIZE> test_data_non_trivial = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};

  std::array<trivial_t, SIZE> test_data_trivial = {0x11223344UL, 0x22334455UL, 0x33445566UL, 0x44556677UL, 0x55667788UL,
                                                   0x66778899UL, 0x778899AAUL, 0x8899AABBUL, 0x99AABBCCUL, 0xAABBCCDDUL};

  non_trivial_t test_item_non_trivial("eleven");
  non_trivial_t test_item_non_trivial_null("");
  trivial_t     test_item_trivial(0xBBCCDDEEUL);

  alignas(non_trivial_t) unsigned char buffer_non_trivial[sizeof(non_trivial_t) * SIZE];
  alignas(trivial_t) unsigned char buffer_trivial[sizeof(trivial_t) * SIZE];
  alignas(moveable_t) unsigned char buffer_moveable[sizeof(moveable_t) * SIZE];

  non_trivial_t* output_non_trivial = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);
  trivial_t*     output_trivial     = reinterpret_cast<trivial_t*>(buffer_trivial);
  moveable_t*    output_moveable    = reinterpret_cast<moveable_t*>(buffer_moveable);

  struct overloaded
  {
    overloaded* operator&()
    {
      return nullptr;
    }
  };

  //***********************************
  template <typename T>
  struct NoDelete
  {
    NoDelete() {}

    void operator()(T*) const {}
  };

  //***********************************
  template <typename T>
  struct NoDelete<T[]>
  {
    NoDelete() {}

    template <class U>
    void operator()(U* /*p*/) const
    {
    }
  };

  //***********************************
  // A minimal fancy pointer with a nested element_type and a static
  // pointer_to. Used to exercise etl::pointer_traits' primary template.
  template <typename T>
  struct fancy_pointer
  {
    typedef T element_type;

    T* raw;

    static fancy_pointer pointer_to(T& r)
    {
      fancy_pointer p;
      p.raw = etl::addressof(r);
      return p;
    }
  };

  //***********************************
  // A fancy pointer without a nested element_type, so pointer_traits must
  // deduce the element type from the first template parameter.
  template <typename T>
  struct bare_pointer
  {
    T* raw;
  };
} // namespace

namespace
{
  SUITE(test_memory)
  {
    //*************************************************************************
    TEST(test_addressof)
    {
      int i;
      CHECK(&i == etl::addressof(i));

      overloaded ol;
      CHECK(&ol != etl::addressof(ol));
      CHECK(reinterpret_cast<overloaded*>(&reinterpret_cast<char&>(ol)) == etl::addressof(ol));
    }

    //*************************************************************************
    TEST(test_create_destroy_trivial)
    {
      char       n[sizeof(trivial_t)];
      trivial_t* pn = reinterpret_cast<trivial_t*>(n);

      // Non count.
      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_default_at(pn);
      CHECK_EQUAL(0xFFFFFFFFUL, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(0xFFFFFFFFUL, *pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_value_at(pn);
      CHECK_EQUAL(0x00000000UL, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(0x00000000UL, *pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_copy_at(pn, test_item_trivial);
      CHECK_EQUAL(test_item_trivial, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(test_item_trivial, *pn);

      // Count.
      size_t count = 0UL;

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_default_at(pn, count);
      CHECK_EQUAL(0xFFFFFFFFUL, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(0xFFFFFFFFUL, *pn);
      CHECK_EQUAL(0U, count);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_value_at(pn, count);
      CHECK_EQUAL(0x00000000UL, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(0x00000000UL, *pn);
      CHECK_EQUAL(0U, count);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_copy_at(pn, test_item_trivial, count);
      CHECK_EQUAL(test_item_trivial, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(test_item_trivial, *pn);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_create_destroy_non_trivial)
    {
      alignas(non_trivial_t) unsigned char n[sizeof(non_trivial_t)];
      non_trivial_t*                       pn = reinterpret_cast<non_trivial_t*>(n);

      // Non count.
      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_default_at(pn);
      CHECK_EQUAL(test_item_non_trivial_null, *pn);
      etl::destroy_at(pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_value_at(pn);
      CHECK_EQUAL(test_item_non_trivial_null, *pn);
      etl::destroy_at(pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_copy_at(pn, test_item_non_trivial);
      CHECK_EQUAL(test_item_non_trivial, *pn);
      etl::destroy_at(pn);

      // Count.
      size_t count = 0UL;

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_default_at(pn, count);
      CHECK_EQUAL(test_item_non_trivial_null, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(0U, count);

      count = 0;
      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_value_at(pn, count);
      CHECK_EQUAL(test_item_non_trivial_null, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(0U, count);

      count = 0;
      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::create_copy_at(pn, test_item_non_trivial, count);
      CHECK_EQUAL(test_item_non_trivial, *pn);
      CHECK_EQUAL(1U, count);
      etl::destroy_at(pn, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_construct_destroy_trivial)
    {
      char       n[sizeof(trivial_t)];
      trivial_t* pn = reinterpret_cast<trivial_t*>(n);

      // Non count.
      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::construct_at(pn);
      CHECK_EQUAL(0UL, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(0UL, *pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::construct_at(pn);
      CHECK_EQUAL(0x00000000UL, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(0x00000000UL, *pn);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::construct_at(pn, test_item_trivial);
      CHECK_EQUAL(test_item_trivial, *pn);
      etl::destroy_at(pn);
      CHECK_EQUAL(test_item_trivial, *pn);
    }

    //*************************************************************************
    TEST(test_uninitialized_fill_n_trivial)
    {
      // Also tests uninitialized_fill.

      // Non count.
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0);
      etl::uninitialized_fill_n(p, SIZE, test_item_trivial);

      trivial_t* result;

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == test_item_trivial; });

      CHECK(result == output_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0);
      etl::uninitialized_fill_n(p, SIZE, test_item_trivial, count);

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == test_item_trivial; });

      CHECK(result == output_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_fill_n_non_trivial)
    {
      // Also tests uninitialized_fill.

      // Non count.
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0);
      etl::uninitialized_fill_n(p, SIZE, test_item_non_trivial);

      non_trivial_t* result;

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](const non_trivial_t& i) { return i == test_item_non_trivial; });

      CHECK(result == output_non_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0);
      etl::uninitialized_fill_n(p, SIZE, test_item_non_trivial, count);

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](non_trivial_t i) { return i == test_item_non_trivial; });

      CHECK(result == output_non_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_copy_n_trivial)
    {
      // Also tests uninitialized_copy.

      bool is_equal;

      // Non count.
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0);
      etl::uninitialized_copy_n(test_data_trivial.begin(), SIZE, p);

      is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0);
      etl::uninitialized_copy_n(test_data_trivial.begin(), SIZE, p, count);

      is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK_EQUAL(SIZE, count);
      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_copy_n_non_trivial)
    {
      // Also tests uninitialized_copy.

      bool is_equal;

      // Non count.
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0);
      etl::uninitialized_copy_n(test_data_non_trivial.begin(), SIZE, p);

      is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin());
      CHECK(is_equal);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0);
      etl::uninitialized_copy_n(test_data_non_trivial.begin(), SIZE, p, count);

      is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin());
      CHECK(is_equal);
      CHECK_EQUAL(SIZE, count);
      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_move)
    {
      bool is_equal;

      // Non count.
      moveable_t* p = reinterpret_cast<moveable_t*>(buffer_moveable);

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

      {
        std::array<moveable_t, SIZE> test_data_moveable = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                                           moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

        etl::uninitialized_move(test_data_moveable.begin(), test_data_moveable.end(), p);
      }

      is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                 && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                 && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                 && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0);

      {
        std::array<moveable_t, SIZE> test_data_moveable = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                                           moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

        etl::uninitialized_move(test_data_moveable.begin(), test_data_moveable.end(), p, count);
      }

      is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                 && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                 && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                 && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);
      CHECK_EQUAL(SIZE, count);
      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_move_n)
    {
      bool is_equal;

      // Non count.
      moveable_t* p = reinterpret_cast<moveable_t*>(buffer_moveable);

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

      {
        std::array<moveable_t, SIZE> test_data_moveable = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                                           moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

        etl::uninitialized_move_n(test_data_moveable.begin(), SIZE, p);
      }

      is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                 && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                 && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                 && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0);

      {
        std::array<moveable_t, SIZE> test_data_moveable = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                                           moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

        etl::uninitialized_move_n(test_data_moveable.begin(), SIZE, p, count);
      }

      is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                 && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                 && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                 && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);
      CHECK_EQUAL(SIZE, count);
      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_default_construct_n_trivial)
    {
      // Also tests uninitialized_default_construct.

      // Non count.
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      etl::uninitialized_default_construct_n(p, SIZE);

      trivial_t* result;

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == 0xFFFFFFFFUL; });

      CHECK(result == output_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      etl::uninitialized_default_construct_n(p, SIZE, count);

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == 0xFFFFFFFFUL; });

      CHECK(result == output_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_default_construct_n_non_trivial)
    {
      // Also tests uninitialized_default_construct.

      // Non count.
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0xFFU);
      etl::uninitialized_default_construct_n(p, SIZE);

      non_trivial_t* result;

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](non_trivial_t i) { return i == test_item_non_trivial_null; });

      CHECK(result == output_non_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0xFFU);
      etl::uninitialized_default_construct_n(p, SIZE, count);

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](non_trivial_t i) { return i == test_item_non_trivial_null; });

      CHECK(result == output_non_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_value_construct_n_trivial)
    {
      // Also tests uninitialized_default_construct.

      // Non count.
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      etl::uninitialized_value_construct_n(p, SIZE);

      trivial_t* result;

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == trivial_t(); });

      CHECK(result == output_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      etl::uninitialized_value_construct_n(p, SIZE, count);

      result = std::find_if_not(output_trivial, output_trivial + SIZE, [](trivial_t i) { return i == trivial_t(); });

      CHECK(result == output_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_uninitialized_value_construct_n_non_trivial)
    {
      // Also tests uninitialized_default_construct.

      // Non count.
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0xFFU);
      etl::uninitialized_value_construct_n(p, SIZE);

      non_trivial_t* result;

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](non_trivial_t i) { return i == non_trivial_t(); });

      CHECK(result == output_non_trivial + SIZE);
      etl::destroy(p, p + SIZE);

      // Count.
      size_t count = 0UL;
      std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0xFFU);
      etl::uninitialized_value_construct_n(p, SIZE, count);

      result = std::find_if_not(output_non_trivial, output_non_trivial + SIZE, [](non_trivial_t i) { return i == non_trivial_t(); });

      CHECK(result == output_non_trivial + SIZE);
      CHECK_EQUAL(SIZE, count);

      etl::destroy(p, p + SIZE, count);
      CHECK_EQUAL(0U, count);
    }

    //*************************************************************************
    TEST(test_create_copy)
    {
      struct Object : etl::create_copy<Object>
      {
        std::string text;
      };

      alignas(Object) unsigned char buffer[sizeof(Object)];

      Object object1;
      object1.text = "12345678";
      object1.create_copy_at(buffer);
      object1.text = "87654321";

      Object& object2 = *reinterpret_cast<Object*>(buffer);

      CHECK_EQUAL(std::string("87654321"), object1.text);
      CHECK_EQUAL(std::string("12345678"), object2.text);

      int count = 0;
      object1.create_copy_at(buffer, count);

      CHECK_EQUAL(1, count);
    }

    //*************************************************************************
    TEST(test_create_make_copy)
    {
      struct Object : etl::create_copy<Object>
      {
        std::string text;
      };

      alignas(Object) unsigned char buffer[sizeof(Object)];

      Object object1;
      object1.text    = "12345678";
      Object& object2 = object1.make_copy_at(buffer);
      object1.text    = "87654321";

      CHECK_EQUAL(std::string("87654321"), object1.text);
      CHECK_EQUAL(std::string("12345678"), object2.text);

      int count = 0;
      object1.make_copy_at(buffer, count);

      CHECK_EQUAL(1, count);
    }

    //*************************************************************************
    TEST(test_make_trivial)
    {
      char       n[sizeof(trivial_t)];
      trivial_t* pn = reinterpret_cast<trivial_t*>(n);

      // Non count.
      std::fill(std::begin(n), std::end(n), 0xFFU);
      CHECK_EQUAL(0x00000000UL, etl::make_default_at(pn));

      std::fill(std::begin(n), std::end(n), 0x00U);
      CHECK_EQUAL(0xFFFFFFFFUL, etl::make_value_at(pn, 0xFFFFFFFFU));

      std::fill(std::begin(n), std::end(n), 0xFFU);
      etl::make_copy_at(pn, test_item_trivial);
      CHECK_EQUAL(test_item_trivial, etl::make_copy_at(pn, test_item_trivial));

      // Count.
      size_t count = 0UL;

      std::fill(std::begin(n), std::end(n), 0xFFU);
      CHECK_EQUAL(0x00000000UL, etl::make_default_at(pn, count));
      CHECK_EQUAL(1U, count);

      std::fill(std::begin(n), std::end(n), 0x00U);
      CHECK_EQUAL(0xFFFFFFFFUL, etl::make_value_at(pn, 0xFFFFFFFFUL, count));
      CHECK_EQUAL(2U, count);

      std::fill(std::begin(n), std::end(n), 0xFFU);
      CHECK_EQUAL(test_item_trivial, etl::make_copy_at(pn, test_item_trivial, count));
      CHECK_EQUAL(3U, count);
    }

    //*************************************************************************
    TEST(test_memory_clear)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data = {0xFFFFFFFFUL, char(0xFFU)};

      etl::memory_clear(data);

      CHECK_EQUAL(0x00000000UL, data.d1);
      CHECK_EQUAL(0x00, data.d2);
    }

    //*************************************************************************
    TEST(test_memory_clear_range_pointer_n)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data[3] = {{0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}};

      etl::memory_clear_range(data, 3);

      CHECK_EQUAL(0x00000000UL, data[0].d1);
      CHECK_EQUAL(0x00U, data[0].d2);

      CHECK_EQUAL(0x00000000UL, data[1].d1);
      CHECK_EQUAL(0x00U, data[1].d2);

      CHECK_EQUAL(0x00000000UL, data[2].d1);
      CHECK_EQUAL(0x00U, data[2].d2);
    }

    //*************************************************************************
    TEST(test_memory_clear_range_pointer_pointer)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data[3] = {{0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}};

      etl::memory_clear_range(std::begin(data), std::end(data));

      CHECK_EQUAL(0x00000000UL, data[0].d1);
      CHECK_EQUAL(0x00U, data[0].d2);

      CHECK_EQUAL(0x00000000UL, data[1].d1);
      CHECK_EQUAL(0x00U, data[1].d2);

      CHECK_EQUAL(0x00000000UL, data[2].d1);
      CHECK_EQUAL(0x00U, data[2].d2);
    }

    //*************************************************************************
    TEST(test_memory_set)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data = {0xFFFFFFFFUL, char(0xFFU)};

      etl::memory_set(data, 0x5A);

      CHECK_EQUAL(0x5A5A5A5AUL, data.d1);
      CHECK_EQUAL(0x5AU, data.d2);
    }

    //*************************************************************************
    TEST(test_memory_set_range_pointer_n)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data[3] = {{0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}};

      etl::memory_set_range(data, 3, 0x5A);

      CHECK_EQUAL(0x5A5A5A5AUL, data[0].d1);
      CHECK_EQUAL(0x5AU, data[0].d2);

      CHECK_EQUAL(0x5A5A5A5AUL, data[1].d1);
      CHECK_EQUAL(0x5AU, data[1].d2);

      CHECK_EQUAL(0x5A5A5A5AUL, data[2].d1);
      CHECK_EQUAL(0x5AU, data[2].d2);
    }

    //*************************************************************************
    TEST(test_memory_set_range_pointer_pointer)
    {
      struct Data
      {
        uint32_t d1;
        char     d2;
      };

      Data data[3] = {{0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}, {0xFFFFFFFFUL, char(0xFFU)}};

      etl::memory_set_range(std::begin(data), std::end(data), 0x5A);

      CHECK_EQUAL(0x5A5A5A5AUL, data[0].d1);
      CHECK_EQUAL(0x5AU, data[0].d2);

      CHECK_EQUAL(0x5A5A5A5AUL, data[1].d1);
      CHECK_EQUAL(0x5AU, data[1].d2);

      CHECK_EQUAL(0x5A5A5A5AUL, data[2].d1);
      CHECK_EQUAL(0x5AU, data[2].d2);
    }

    //*************************************************************************
    TEST(test_unique_ptr_default_construction)
    {
      etl::unique_ptr<int> up;

      CHECK(up.get() == nullptr);
      CHECK(!bool(up));
    }

    //*************************************************************************
    TEST(test_unique_ptr_from_pointer_construction)
    {
      etl::unique_ptr<int> up(new int(1));

      CHECK(up.get() != nullptr);
      CHECK(bool(up));
      CHECK_EQUAL(1, *up);
    }

    //*************************************************************************
    TEST(test_unique_ptr_move_construction)
    {
      etl::unique_ptr<int> up1(new int(1));
      etl::unique_ptr<int> up2(std::move(up1));

      CHECK(up1.get() == nullptr);
      CHECK(!bool(up1));
      CHECK(up2.get() != nullptr);
      CHECK(bool(up2));
      CHECK_EQUAL(1, *up2);
    }

    //*************************************************************************
    TEST(test_unique_ptr_release)
    {
      auto                 buffer = new int;
      etl::unique_ptr<int> up(buffer);

      CHECK(up.release() != nullptr);
      CHECK(!bool(up));

      delete buffer;
    }

    //*************************************************************************
    TEST(test_unique_ptr_reset)
    {
      etl::unique_ptr<int> up(new int(1));
      int*                 p = new int(2);

      CHECK_EQUAL(1, *up);
      up.reset(p);
      CHECK_EQUAL(2, *up);
    }

    //*************************************************************************
    TEST(test_unique_ptr_swap)
    {
      etl::unique_ptr<int> up1(new int(1));
      etl::unique_ptr<int> up2(new int(2));

      up1.swap(up2);

      CHECK_EQUAL(2, *up1);
      CHECK_EQUAL(1, *up2);
    }

    //*************************************************************************
    TEST(test_unique_ptr_swap_deleters)
    {
      struct Deleter
      {
        char id;

        explicit Deleter(const char id_)
          : id(id_)
        {
        }

        Deleter(const Deleter&)            = delete;
        Deleter& operator=(const Deleter&) = delete;

        Deleter(Deleter&& other) noexcept
          : id(other.id)
        {
          other.id = ' ';
        }

        Deleter& operator=(Deleter&& other) noexcept
        {
          if (&other != this)
          {
            id       = other.id;
            other.id = ' ';
          }

          return *this;
        }

        ~Deleter() = default;

        void operator()(int*) const {}
      };

      int                           a_obj = 1;
      int                           b_obj = 2;
      etl::unique_ptr<int, Deleter> up1(&a_obj, Deleter('A'));
      etl::unique_ptr<int, Deleter> up2(&b_obj, Deleter('B'));

      up1.swap(up2);

      CHECK_EQUAL(2, *up1);
      CHECK_EQUAL('B', up1.get_deleter().id);
      CHECK_EQUAL(1, *up2);
      CHECK_EQUAL('A', up2.get_deleter().id);
    }

    //*************************************************************************
    TEST(test_unique_ptr_from_nullptr_assignment)
    {
      etl::unique_ptr<int> up(new int);

      up = nullptr;

      CHECK(up.get() == nullptr);
      CHECK(!bool(up));
    }

    //*************************************************************************
    TEST(test_unique_ptr_nullptr_from_nullptr_assignment)
    {
      etl::unique_ptr<int> up;

      up = nullptr;

      CHECK(up.get() == nullptr);
      CHECK(!bool(up));
    }

    //*************************************************************************
    TEST(test_unique_ptr_move_assignment)
    {
      etl::unique_ptr<int> up1(new int(1));
      etl::unique_ptr<int> up2(new int(2));

      up1 = std::move(up2);

      CHECK(!bool(up2));
      CHECK_EQUAL(2, *up1);
    }

    //*************************************************************************
    TEST(test_unique_ptr_comparison_tests)
    {
      int* p1 = (int*)1U;
      int* p2 = (int*)2U;

      etl::unique_ptr<int, NoDelete<int>> up1(p1);
      etl::unique_ptr<int, NoDelete<int>> up2(p1);
      etl::unique_ptr<int, NoDelete<int>> up3(p2);

      CHECK(up1 == up2);
      CHECK(!(up1 == up3));
      CHECK(!(up1 < up2));
      CHECK(up1 < up3);
      CHECK(!(up3 <= up1));
      CHECK(up1 <= up2);
      CHECK(up1 <= up3);
      CHECK(!(up1 > up2));
      CHECK(up3 > up1);
      CHECK(!(up1 >= up3));
      CHECK(up2 >= up1);
      CHECK(up3 >= up1);
    }

    //*************************************************************************
    TEST(test_unique_ptr_from_array_pointer_construction)
    {
      etl::unique_ptr<int[]> up(new int[4]);
      std::iota(&up[0], &up[4], 0);

      CHECK(up.get() != nullptr);
      CHECK(bool(up));
      CHECK_EQUAL(0, up[0]);
      CHECK_EQUAL(1, up[1]);
      CHECK_EQUAL(2, up[2]);
      CHECK_EQUAL(3, up[3]);
    }

    //*************************************************************************
    TEST(test_unique_ptr_move_array_construction)
    {
      etl::unique_ptr<int[]> up1(new int[4]);
      std::iota(&up1[0], &up1[4], 0);
      etl::unique_ptr<int[]> up2(std::move(up1));

      CHECK(up1.get() == nullptr);
      CHECK(!bool(up1));
      CHECK(up2.get() != nullptr);
      CHECK(bool(up2));
      CHECK_EQUAL(0, up2[0]);
      CHECK_EQUAL(1, up2[1]);
      CHECK_EQUAL(2, up2[2]);
      CHECK_EQUAL(3, up2[3]);
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_release)
    {
      auto                   buffer = new int[4];
      etl::unique_ptr<int[]> up(buffer);
      std::iota(&up[0], &up[4], 0);

      CHECK(up.release() != nullptr);
      CHECK(!bool(up));

      delete[] buffer;
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_reset)
    {
      etl::unique_ptr<int[]> up(new int[4]);
      std::iota(&up[0], &up[4], 0);

      int* p = new int[4];
      std::iota(p, p + 4, 4);

      CHECK_EQUAL(0, up[0]);
      CHECK_EQUAL(1, up[1]);
      CHECK_EQUAL(2, up[2]);
      CHECK_EQUAL(3, up[3]);
      up.reset(p);
      CHECK_EQUAL(4, up[0]);
      CHECK_EQUAL(5, up[1]);
      CHECK_EQUAL(6, up[2]);
      CHECK_EQUAL(7, up[3]);
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_swap)
    {
      etl::unique_ptr<int[]> up1(new int[4]);
      std::iota(&up1[0], &up1[4], 0);

      etl::unique_ptr<int[]> up2(new int[4]);
      std::iota(&up2[0], &up2[4], 4);

      up1.swap(up2);

      CHECK_EQUAL(4, up1[0]);
      CHECK_EQUAL(5, up1[1]);
      CHECK_EQUAL(6, up1[2]);
      CHECK_EQUAL(7, up1[3]);

      CHECK_EQUAL(0, up2[0]);
      CHECK_EQUAL(1, up2[1]);
      CHECK_EQUAL(2, up2[2]);
      CHECK_EQUAL(3, up2[3]);
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_swap_deleters)
    {
      struct Deleter
      {
        char id;

        explicit Deleter(const char id_)
          : id(id_)
        {
        }

        Deleter(const Deleter&)            = delete;
        Deleter& operator=(const Deleter&) = delete;

        Deleter(Deleter&& other) noexcept
          : id(other.id)
        {
          other.id = ' ';
        }

        Deleter& operator=(Deleter&& other) noexcept
        {
          if (&other != this)
          {
            id       = other.id;
            other.id = ' ';
          }

          return *this;
        }

        ~Deleter() = default;

        void operator()(int*) const {}
      };

      int                             a_obj = 1;
      int                             b_obj = 2;
      etl::unique_ptr<int[], Deleter> up1(&a_obj, Deleter('A'));
      etl::unique_ptr<int[], Deleter> up2(&b_obj, Deleter('B'));

      up1.swap(up2);

      CHECK_EQUAL(2, up1[0]);
      CHECK_EQUAL('B', up1.get_deleter().id);
      CHECK_EQUAL(1, up2[0]);
      CHECK_EQUAL('A', up2.get_deleter().id);
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_from_nullptr_assignment)
    {
      etl::unique_ptr<int[]> up(new int[4]);

      up = nullptr;

      CHECK(up.get() == nullptr);
      CHECK(!bool(up));
    }

    //*************************************************************************
    TEST(test_unique_ptr_array_move_assignment)
    {
      etl::unique_ptr<int[]> up1(new int[4]);
      std::iota(&up1[0], &up1[4], 0);

      etl::unique_ptr<int[]> up2(new int[4]);
      std::iota(&up2[0], &up2[4], 4);

      up1 = std::move(up2);

      CHECK(!bool(up2));
      CHECK_EQUAL(4, up1[0]);
      CHECK_EQUAL(5, up1[1]);
      CHECK_EQUAL(6, up1[2]);
      CHECK_EQUAL(7, up1[3]);
    }

    //*************************************************************************
    TEST(test_unique_ptr_custom_deleter)
    {
      //*******************************
      struct Object
      {
        Object()
          : count(1)
        {
        }

        void Delete()
        {
          count = 0;
        }

        int count;
      };

      //*******************************
      struct Deleter
      {
        void operator()(Object* p)
        {
          p->Delete();
        }
      };

      Deleter deleter;
      Object  object;

      CHECK_EQUAL(1, object.count);

      {
        etl::unique_ptr<Object, Deleter> up(&object, deleter);
      }

      CHECK_EQUAL(0, object.count);
    }

    //*************************************************************************
    TEST(test_uninitialized_buffer)
    {
      typedef etl::uninitialized_buffer<sizeof(uint32_t), 4, etl::alignment_of<uint32_t>::value> storage32_t;

      size_t alignment = etl::alignment_of<storage32_t>::value;
      size_t expected  = std::alignment_of<uint32_t>::value;

      CHECK_EQUAL(expected, alignment);
    }

    //*************************************************************************
    TEST(test_uninitialized_buffer_of)
    {
      typedef etl::uninitialized_buffer_of<uint32_t, 4> storage32_t;
      static storage32_t                                buffer;

      uint32_t*       i  = buffer;
      const uint32_t* ci = buffer;

      CHECK(i == ci);

      buffer[0] = 0U;
      buffer[1] = 1U;
      buffer[2] = 2U;
      buffer[3] = 3U;

      CHECK_EQUAL(0U, buffer[0]);
      CHECK_EQUAL(1U, buffer[1]);
      CHECK_EQUAL(2U, buffer[2]);
      CHECK_EQUAL(3U, buffer[3]);

      const storage32_t& refbuffer = buffer;

      CHECK_EQUAL(0U, refbuffer[0]);
      CHECK_EQUAL(1U, refbuffer[1]);
      CHECK_EQUAL(2U, refbuffer[2]);
      CHECK_EQUAL(3U, refbuffer[3]);

      size_t alignment = etl::alignment_of<storage32_t>::value;
      size_t expected  = std::alignment_of<uint32_t>::value;

      CHECK_EQUAL(expected, alignment);
    }

    //*************************************************************************
    TEST(test_mem_copy_pointer_pointer_pointer)
    {
      uint32_t src[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t dst[8] = {0, 0, 0, 0, 0, 0, 0, 0};

      uint32_t* result = etl::mem_copy(src, src + 8, dst);
      CHECK(std::equal(src, src + 8, dst));
      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_mem_copy_const_pointer_const_pointer_pointer)
    {
      const uint32_t src[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       dst[8] = {0, 0, 0, 0, 0, 0, 0, 0};

      uint32_t* result = etl::mem_copy(src, src + 8, dst);
      CHECK(std::equal(src, src + 8, dst));
      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_mem_copy_pointer_length_pointer)
    {
      uint32_t src[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t dst[8] = {0, 0, 0, 0, 0, 0, 0, 0};

      uint32_t* result = etl::mem_copy(src, 8, dst);
      CHECK(std::equal(src, src + 8, dst));
      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_mem_copy_const_pointer_length_pointer)
    {
      const uint32_t src[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       dst[8] = {0, 0, 0, 0, 0, 0, 0, 0};

      uint32_t* result = etl::mem_copy(src, 8, dst);
      CHECK(std::equal(src, src + 8, dst));
      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_mem_move_pointer_pointer_pointer)
    {
      uint32_t expected[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t data[12]    = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201, 0, 0, 0, 0};

      uint32_t* result = etl::mem_move(data, data + 8, data + 4);
      CHECK(std::equal(expected, expected + 8, data + 4));
      CHECK(result == data + 4);
    }

    //*************************************************************************
    TEST(test_mem_move_const_pointer_const_pointer_pointer)
    {
      uint32_t        expected[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t        data[12]    = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201, 0, 0, 0, 0};
      const uint32_t* data_begin  = &data[0];
      const uint32_t* data_end    = &data[8];

      uint32_t* result = etl::mem_move(data_begin, data_end, data + 4);
      CHECK(std::equal(expected, expected + 8, data + 4));
      CHECK(result == data + 4);
    }

    //*************************************************************************
    TEST(test_mem_move_pointer_length_pointer)
    {
      uint32_t expected[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t data[12]    = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201, 0, 0, 0, 0};

      uint32_t* result = etl::mem_move(data, 8, data + 4);
      CHECK(std::equal(expected, expected + 8, data + 4));
      CHECK(result == data + 4);
    }

    //*************************************************************************
    TEST(test_mem_move_const_pointer_length_pointer)
    {
      uint32_t        expected[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t        data[12]    = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201, 0, 0, 0, 0};
      const uint32_t* data_begin  = &data[0];

      uint32_t* result = etl::mem_move(data_begin, 8, data + 4);
      CHECK(std::equal(expected, expected + 8, data + 4));
      CHECK(result == data + 4);
    }

    //*************************************************************************
    TEST(test_mem_compare_pointer_pointer_pointer)
    {
      uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, data + 8, same) == 0);
      CHECK(etl::mem_compare(data, data + 8, grtr) > 0);
      CHECK(etl::mem_compare(data, data + 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_compare_const_pointer_const_pointer_pointer)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, data + 8, same) == 0);
      CHECK(etl::mem_compare(data, data + 8, grtr) > 0);
      CHECK(etl::mem_compare(data, data + 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_compare_const_pointer_const_pointer_const_pointer)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      const uint32_t same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, data + 8, same) == 0);
      CHECK(etl::mem_compare(data, data + 8, grtr) > 0);
      CHECK(etl::mem_compare(data, data + 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_compare_pointer_length_pointer)
    {
      uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, 8, same) == 0);
      CHECK(etl::mem_compare(data, 8, grtr) > 0);
      CHECK(etl::mem_compare(data, 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_compare_const_pointer_length_pointer)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      uint32_t       less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, 8, same) == 0);
      CHECK(etl::mem_compare(data, 8, grtr) > 0);
      CHECK(etl::mem_compare(data, 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_compare_const_pointer_length_const_pointer)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      const uint32_t same[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67234501, 0x45016723, 0x01324576, 0x76453201};
      const uint32_t grtr[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67235501, 0x45016723, 0x01324576, 0x76453201};
      const uint32_t less[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67134501, 0x45016723, 0x01324576, 0x76453201};

      CHECK(etl::mem_compare(data, 8, same) == 0);
      CHECK(etl::mem_compare(data, 8, grtr) > 0);
      CHECK(etl::mem_compare(data, 8, less) < 0);
    }

    //*************************************************************************
    TEST(test_mem_set_pointer_pointer)
    {
      uint32_t data[8]     = {0, 0, 0, 0, 0, 0, 0, 0};
      uint32_t expected[8] = {0, 0x5A5A5A5A, 0x5A5A5A5A, 0x5A5A5A5A, 0x5A5A5A5A, 0, 0, 0};

      etl::mem_set(data + 1, data + 5, (char)0x5A);

      CHECK(std::equal(expected, expected + 8, data));
    }

    //*************************************************************************
    TEST(test_mem_set_pointer_length)
    {
      uint32_t data[8]     = {0, 0, 0, 0, 0, 0, 0, 0};
      uint32_t expected[8] = {0, 0x5A5A5A5A, 0x5A5A5A5A, 0x5A5A5A5A, 0x5A5A5A5A, 0, 0, 0};

      etl::mem_set(data + 1, 4, (char)0x5A);

      CHECK(std::equal(expected, expected + 8, data));
    }

    //*************************************************************************
    TEST(test_mem_char_pointer_pointer)
    {
      uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67294501, 0x45016723, 0x01324576, 0x76453201};

      char* p1 = etl::mem_char(data, data + 8, (char)0x29);
      char* p2 = etl::mem_char(data, data + 8, (char)0x99);

      CHECK_EQUAL(uint32_t(0x29), uint32_t(*p1));
      if (etl::endianness::value() == etl::endian::little)
      {
        CHECK((reinterpret_cast<char*>(data) + 18) == p1);
      }
      else
      {
        CHECK((reinterpret_cast<char*>(data) + 17) == p1);
      }
      CHECK((reinterpret_cast<char*>(data) + 32) == p2);
    }

    //*************************************************************************
    TEST(test_mem_char_pointer_pointer_const)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67294501, 0x45016723, 0x01324576, 0x76453201};

      const char* p1 = etl::mem_char(data, data + 8, (char)0x29);
      const char* p2 = etl::mem_char(data, data + 8, (char)0x99);

      CHECK_EQUAL(uint32_t(0x29), uint32_t(*p1));
      if (etl::endianness::value() == etl::endian::little)
      {
        CHECK((reinterpret_cast<const char*>(data) + 18) == p1);
      }
      else
      {
        CHECK((reinterpret_cast<const char*>(data) + 17) == p1);
      }
      CHECK((reinterpret_cast<const char*>(data) + 32) == p2);
    }

    //*************************************************************************
    TEST(test_mem_char_pointer_length)
    {
      uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67294501, 0x45016723, 0x01324576, 0x76453201};

      char* p1 = etl::mem_char(data, 8, (char)0x29);
      char* p2 = etl::mem_char(data, 8, (char)0x99);

      CHECK_EQUAL(uint32_t(0x29), uint32_t(*p1));
      if (etl::endianness::value() == etl::endian::little)
      {
        CHECK((reinterpret_cast<char*>(data) + 18) == p1);
      }
      else
      {
        CHECK((reinterpret_cast<char*>(data) + 17) == p1);
      }
      CHECK((reinterpret_cast<char*>(data) + 32) == p2);
    }

    //*************************************************************************
    TEST(test_mem_char_pointer_length_const)
    {
      const uint32_t data[8] = {0x12345678, 0x76543210, 0x01452367, 0x23670145, 0x67294501, 0x45016723, 0x01324576, 0x76453201};

      const char* p1 = etl::mem_char(data, 8, (char)0x29);
      const char* p2 = etl::mem_char(data, 8, (char)0x99);

      CHECK_EQUAL(uint32_t(0x29), uint32_t(*p1));
      if (etl::endianness::value() == etl::endian::little)
      {
        CHECK((reinterpret_cast<const char*>(data) + 18) == p1);
      }
      else
      {
        CHECK((reinterpret_cast<const char*>(data) + 17) == p1);
      }
      CHECK((reinterpret_cast<const char*>(data) + 32) == p2);
    }

    //*************************************************************************
    class Base
    {
    public:

      virtual ~Base() {}
      virtual void function() = 0;
    };

    static bool function_was_called = false;

    class Derived : public Base
    {
    public:

      Derived()
      {
        function_was_called = false;
      }

      void function()
      {
        function_was_called = true;
      }
    };

    void call(etl::unique_ptr<Base> ptr)
    {
      ptr->function();
    }

    TEST(test_derived_type)
    {
      CHECK(!function_was_called);

      etl::unique_ptr<Derived> ptr(new Derived());
      CHECK(ptr.get() != ETL_NULLPTR);

      call(etl::move(ptr));
      CHECK(function_was_called);
      CHECK(ptr.get() == ETL_NULLPTR);
    }

    struct Flags
    {
      Flags()
        : constructed(false)
        , destructed(false)
      {
      }

      void Clear()
      {
        constructed = false;
        destructed  = false;
      }

      bool constructed;
      bool destructed;
    };

    static Flags flags;

    TEST(test_construct_get_destroy_object_aligned)
    {
      struct Data
      {
        Data()
          : a(1)
          , b(2)
        {
          flags.constructed = true;
        }

        Data(int a_, int b_)
          : a(a_)
          , b(b_)
        {
          flags.constructed = true;
        }

        ~Data()
        {
          flags.destructed = true;
        }

        int a;
        int b;
      };

      alignas(Data) char buffer1[sizeof(Data)];
      char*              pbuffer1 = buffer1;

      alignas(Data) char buffer1b[sizeof(Data)];
      char*              pbuffer1b = buffer1b;

      alignas(Data) char buffer2[sizeof(Data)];
      char*              pbuffer2 = buffer2;

      alignas(Data) char buffer2b[sizeof(Data)];
      char*              pbuffer2b = buffer2b;

      alignas(Data) char buffer3[sizeof(Data)];
      char*              pbuffer3 = buffer3;

      alignas(Data) char buffer3b[sizeof(Data)];
      char*              pbuffer3b = buffer3b;

      flags.Clear();
      Data& rdata1 = etl::construct_object_at<Data>(pbuffer1);
      CHECK_TRUE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(1, rdata1.a);
      CHECK_EQUAL(2, rdata1.b);

      flags.Clear();
      Data  data2(3, 4);
      Data& rdata2 = etl::construct_object_at(pbuffer2, data2);
      CHECK_TRUE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(data2.a, rdata2.a);
      CHECK_EQUAL(data2.b, rdata2.b);

      flags.Clear();
      Data& rdata3 = etl::construct_object_at<Data>(pbuffer3, 5, 6);
      CHECK_TRUE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(5, rdata3.a);
      CHECK_EQUAL(6, rdata3.b);

      memcpy(buffer1b, buffer1, sizeof(Data));
      memcpy(buffer2b, buffer2, sizeof(Data));
      memcpy(buffer3b, buffer3, sizeof(Data));

      flags.Clear();
      Data& rdata1b = etl::get_object_at<Data>(pbuffer1b);
      CHECK_FALSE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(1, rdata1b.a);
      CHECK_EQUAL(2, rdata1b.b);

      flags.Clear();
      Data& rdata2b = etl::get_object_at<Data>(pbuffer2b);
      CHECK_FALSE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(data2.a, rdata2b.a);
      CHECK_EQUAL(data2.b, rdata2b.b);

      flags.Clear();
      Data& rdata3b = etl::get_object_at<Data>(pbuffer3b);
      CHECK_FALSE(flags.constructed);
      CHECK_FALSE(flags.destructed);
      CHECK_EQUAL(5, rdata3b.a);
      CHECK_EQUAL(6, rdata3b.b);

      flags.Clear();
      etl::destroy_object_at<Data>(pbuffer1b);
      CHECK_FALSE(flags.constructed);
      CHECK_TRUE(flags.destructed);

      flags.Clear();
      etl::destroy_object_at<Data>(pbuffer2b);
      CHECK_FALSE(flags.constructed);
      CHECK_TRUE(flags.destructed);

      flags.Clear();
      etl::destroy_object_at<Data>(pbuffer3b);
      CHECK_FALSE(flags.constructed);
      CHECK_TRUE(flags.destructed);
    }

    TEST(test_get_object_at_const_specialization)
    {
      struct Data
      {
        Data()
          : a(1)
          , b(2)
        {
          flags.constructed = true;
        }

        ~Data() = default;

        int a;
        int b;
      };

      std::array<uint8_t, 32U> buffer{};
      etl::construct_object_at(buffer.data(), Data());
      const void* bufferPointer = buffer.data();

      const Data& rdata = etl::get_object_at<Data>(bufferPointer);
      CHECK_TRUE(flags.constructed);
      CHECK_TRUE(rdata.a == 1);
      CHECK_TRUE(rdata.b == 2);
    }

    TEST(test_construct_get_destroy_object_misaligned)
    {
      struct Data
      {
        Data()
          : a(1)
          , b(2)
        {
        }

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

        ~Data() {}

        int a;
        int b;
      };

      alignas(Data) char buffer1[sizeof(Data)];
      char*              pbuffer1 = buffer1 + 1;

      alignas(Data) char buffer2[sizeof(Data)];
      char*              pbuffer2 = buffer2 + 1;

      alignas(Data) char buffer3[sizeof(Data)];
      char*              pbuffer3 = buffer3 + 1;

      CHECK_THROW(etl::construct_object_at<Data>(pbuffer1), etl::alignment_error);

      Data data2(3, 4);
      CHECK_THROW(etl::construct_object_at(pbuffer2, data2), etl::alignment_error);

      CHECK_THROW(etl::construct_object_at<Data>(pbuffer3, 5, 6), etl::alignment_error);

      CHECK_THROW(etl::get_object_at<Data>(pbuffer1), etl::alignment_error);

      CHECK_THROW(etl::destroy_object_at<Data>(pbuffer1), etl::alignment_error);
    }

    //*************************************************************************
    TEST(test_to_address)
    {
      int  i;
      int* pi = &i;

      etl::list<int, 4>           container = {1, 2, 3, 4};
      etl::list<int, 4>::iterator itr       = container.begin();
      std::advance(itr, 2);
      int* plist_item = &*itr;

      CHECK_EQUAL(&i, etl::to_address(pi));
      CHECK_EQUAL(plist_item, etl::to_address(itr));
    }

    //*************************************************************************
    TEST(test_launder)
    {
      int  i = 42;
      int* p = etl::launder(&i);
      CHECK_EQUAL(&i, p);
      CHECK_EQUAL(42, *p);

      // Launder a new object created in reused storage.
      struct quad_t
      {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
      };

      alignas(quad_t) unsigned char buffer[sizeof(quad_t)];
      quad_t*                       q  = ::new (buffer) quad_t{1, 2, 3, 4};
      quad_t*                       lq = etl::launder(q);
      CHECK(reinterpret_cast<unsigned char*>(lq) == buffer);
      CHECK_EQUAL(1, lq->a);
      CHECK_EQUAL(2, lq->b);
      CHECK_EQUAL(3, lq->c);
      CHECK_EQUAL(4, lq->d);
    }

    //*************************************************************************
    TEST(test_start_lifetime_as)
    {
      struct quad_t
      {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
      };

      alignas(quad_t) unsigned char buffer[sizeof(quad_t)] = {1, 2, 3, 4};

      quad_t* p = etl::start_lifetime_as<quad_t>(buffer);
      CHECK(reinterpret_cast<unsigned char*>(p) == buffer);
      CHECK_EQUAL(1, p->a);
      CHECK_EQUAL(2, p->b);
      CHECK_EQUAL(3, p->c);
      CHECK_EQUAL(4, p->d);

      const void*   cbuffer = buffer;
      const quad_t* cp      = etl::start_lifetime_as<quad_t>(cbuffer);
      CHECK(reinterpret_cast<const unsigned char*>(cp) == buffer);
      CHECK_EQUAL(1, cp->a);
      CHECK_EQUAL(4, cp->d);
    }

    //*************************************************************************
    TEST(test_start_lifetime_as_array)
    {
      alignas(uint16_t) unsigned char buffer[sizeof(uint16_t) * 3] = {0, 0, 0, 0, 0, 0};

      uint16_t* p = etl::start_lifetime_as_array<uint16_t>(buffer, 3);
      CHECK(reinterpret_cast<unsigned char*>(p) == buffer);
      p[0] = 1;
      p[2] = 2;
      CHECK_EQUAL(1, p[0]);
      CHECK_EQUAL(2, p[2]);

      // n == 0 returns a pointer comparing equal to p.
      uint16_t* p0 = etl::start_lifetime_as_array<uint16_t>(buffer, 0);
      CHECK(reinterpret_cast<unsigned char*>(p0) == buffer);
    }

    //*************************************************************************
    TEST(test_pointer_traits)
    {
      typedef etl::pointer_traits<int*> traits;

      CHECK((std::is_same<int*, traits::pointer>::value));
      CHECK((std::is_same<int, traits::element_type>::value));
      CHECK((std::is_same<ptrdiff_t, traits::difference_type>::value));
#if ETL_USING_CPP11
      CHECK((std::is_same<char*, traits::rebind<char> >::value));
#endif

      int  i = 42;
      int* p = traits::pointer_to(i);
      CHECK_EQUAL(&i, p);
    }

    //*************************************************************************
    TEST(test_pointer_traits_fancy_pointer)
    {
      typedef etl::pointer_traits<fancy_pointer<int> > traits;

      CHECK((std::is_same<fancy_pointer<int>, traits::pointer>::value));
      CHECK((std::is_same<int, traits::element_type>::value));
      CHECK((std::is_same<ptrdiff_t, traits::difference_type>::value));
      CHECK((std::is_same<fancy_pointer<char>, traits::rebind<char> >::value));

      int                i = 42;
      fancy_pointer<int> p = traits::pointer_to(i);
      CHECK_EQUAL(&i, p.raw);

      // element_type and rebind are deduced from the first template parameter
      // when there is no nested element_type.
      CHECK((std::is_same<int, etl::pointer_traits<bare_pointer<int> >::element_type>::value));
      CHECK((std::is_same<bare_pointer<char>, etl::pointer_traits<bare_pointer<int> >::rebind<char> >::value));
    }

    //*************************************************************************
    TEST(test_align)
    {
      alignas(16) unsigned char buffer[64];

      void*  ptr   = buffer + 1; // Deliberately misaligned.
      size_t space = sizeof(buffer) - 1;

      void* aligned = etl::align(16, 8, ptr, space);

      // 'buffer' is 16-aligned, so aligning 'buffer + 1' to 16 skips 15 bytes of
      // padding, landing on 'buffer + 16' and shrinking 'space' by that padding.
      CHECK(aligned != ETL_NULLPTR);
      CHECK(reinterpret_cast<unsigned char*>(aligned) == buffer + 16);
      CHECK(reinterpret_cast<unsigned char*>(ptr) == buffer + 16);
      CHECK_EQUAL(sizeof(buffer) - 1 - 15, space);

      // Not enough space returns nullptr.
      void*  small_ptr   = buffer + 1;
      size_t small_space = 2;
      CHECK(etl::align(16, 8, small_ptr, small_space) == ETL_NULLPTR);
    }

    //*************************************************************************
    TEST(test_assume_aligned)
    {
      alignas(16) int data[4] = {1, 2, 3, 4};

      int* p = etl::assume_aligned<16>(&data[0]);
      CHECK_EQUAL(&data[0], p);
      CHECK_EQUAL(1, *p);
    }

    //*************************************************************************
    TEST(test_is_sufficiently_aligned)
    {
      alignas(16) unsigned char buffer[32];

      CHECK(etl::is_sufficiently_aligned<16>(buffer));
      CHECK(etl::is_sufficiently_aligned<8>(buffer));
      CHECK(!etl::is_sufficiently_aligned<16>(buffer + 1));
    }

#if ETL_USING_CPP17
    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p, p + SIZE);

      bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_iterator_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p, p + SIZE);

      bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_non_trivial.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_range_trivial)
    {
      trivial_t dst[SIZE] = {};

      auto result = etl::ranges::uninitialized_copy(test_data_trivial, dst);

      bool is_equal = std::equal(std::begin(dst), std::end(dst), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.end());
      CHECK(result.out == std::end(dst));
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_range_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      std::vector<non_trivial_t>     src(test_data_non_trivial.begin(), test_data_non_trivial.end());
      etl::span<non_trivial_t, SIZE> dst(p, SIZE);

      auto result = etl::ranges::uninitialized_copy(src, dst);

      bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.end());
      CHECK(result.out == dst.end());

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_output_shorter)
    {
      // Output range is shorter than input; should stop at output end.
      std::array<trivial_t, 5> small_dst = {};

      auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), small_dst.begin(), small_dst.end());

      bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.begin() + 5);
      CHECK(result.out == small_dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_input_shorter)
    {
      // Input range is shorter than output; should stop at input end.
      std::array<trivial_t, 3> small_src = {1, 2, 3};
      trivial_t                dst[SIZE] = {};

      auto result = etl::ranges::uninitialized_copy(small_src.begin(), small_src.end(), std::begin(dst), std::end(dst));

      CHECK_EQUAL(1U, dst[0]);
      CHECK_EQUAL(2U, dst[1]);
      CHECK_EQUAL(3U, dst[2]);
      CHECK(result.in == small_src.end());
      CHECK(result.out == std::begin(dst) + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.begin(), p, p + SIZE);

      CHECK(result.in == test_data_trivial.begin());
      CHECK(result.out == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), SIZE, p, p + SIZE);

      bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_n_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_copy_n(test_data_non_trivial.begin(), SIZE, p, p + SIZE);

      bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_non_trivial.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_n_output_shorter)
    {
      // Output range is shorter than count; should stop at output end.
      std::array<trivial_t, 5> small_dst = {};

      auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), SIZE, small_dst.begin(), small_dst.end());

      bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.begin() + 5);
      CHECK(result.out == small_dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_n_count_shorter)
    {
      // Count is shorter than output range; should stop after count elements.
      trivial_t dst[SIZE] = {};

      auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), 3, std::begin(dst), std::end(dst));

      bool is_equal = std::equal(std::begin(dst), std::begin(dst) + 3, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == test_data_trivial.begin() + 3);
      CHECK(result.out == std::begin(dst) + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_copy_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), 0, p, p + SIZE);

      CHECK(result.in == test_data_trivial.begin());
      CHECK(result.out == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      auto result = etl::ranges::uninitialized_fill(p, p + SIZE, test_item_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_trivial, p[i]);
      }
      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_iterator_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_fill(p, p + SIZE, test_item_non_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_non_trivial, p[i]);
      }
      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_range_trivial)
    {
      trivial_t dst[SIZE] = {};

      auto result = etl::ranges::uninitialized_fill(dst, test_item_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_trivial, dst[i]);
      }
      CHECK(result == std::end(dst));
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_range_non_trivial)
    {
      alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE];
      non_trivial_t*                       p = reinterpret_cast<non_trivial_t*>(buffer);
      etl::span<non_trivial_t, SIZE>       dst(p, SIZE);

      auto result = etl::ranges::uninitialized_fill(dst, test_item_non_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_non_trivial, p[i]);
      }
      CHECK(result == dst.end());

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_fill(p, p, test_item_trivial);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      auto result = etl::ranges::uninitialized_fill_n(p, SIZE, test_item_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_trivial, p[i]);
      }
      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_n_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_fill_n(p, SIZE, test_item_non_trivial);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_item_non_trivial, p[i]);
      }
      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_n_partial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      auto result = etl::ranges::uninitialized_fill_n(p, 3, test_item_trivial);

      for (size_t i = 0; i < 3; ++i)
      {
        CHECK_EQUAL(test_item_trivial, p[i]);
      }
      CHECK(result == p + 3);

      etl::destroy(p, p + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_fill_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_fill_n(p, 0, test_item_trivial);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), p, p + SIZE);

      bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_iterator_non_trivial)
    {
      moveable_t* p = reinterpret_cast<moveable_t*>(buffer_moveable);

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

      std::array<moveable_t, SIZE> src = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                          moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

      auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), p, p + SIZE);

      bool is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                      && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                      && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                      && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);

      // Source elements should have been moved from (invalidated).
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(false, bool(src[i]));
      }

      CHECK(result.in == src.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_range_trivial)
    {
      trivial_t dst[SIZE] = {};

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move(src, dst);

      bool is_equal = std::equal(std::begin(dst), std::end(dst), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.end());
      CHECK(result.out == std::end(dst));
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_range_non_trivial)
    {
      moveable_t* p = reinterpret_cast<moveable_t*>(buffer_moveable);

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

      std::array<moveable_t, SIZE> src = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                          moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

      etl::span<moveable_t, SIZE> dst(p, SIZE);

      auto result = etl::ranges::uninitialized_move(src, dst);

      bool is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                      && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                      && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                      && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);

      // Source elements should have been moved from (invalidated).
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(false, bool(src[i]));
      }

      CHECK(result.in == src.end());
      CHECK(result.out == dst.end());

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_output_shorter)
    {
      // Output range is shorter than input; should stop at output end.
      std::array<trivial_t, 5> small_dst = {};

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), small_dst.begin(), small_dst.end());

      bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.begin() + 5);
      CHECK(result.out == small_dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_input_shorter)
    {
      // Input range is shorter than output; should stop at input end.
      std::array<trivial_t, 3> small_src = {1, 2, 3};
      trivial_t                dst[SIZE] = {};

      auto result = etl::ranges::uninitialized_move(small_src.begin(), small_src.end(), std::begin(dst), std::end(dst));

      CHECK_EQUAL(1U, dst[0]);
      CHECK_EQUAL(2U, dst[1]);
      CHECK_EQUAL(3U, dst[2]);
      CHECK(result.in == small_src.end());
      CHECK(result.out == std::begin(dst) + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move(src.begin(), src.begin(), p, p + SIZE);

      CHECK(result.in == src.begin());
      CHECK(result.out == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, p, p + SIZE);

      bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_n_non_trivial)
    {
      moveable_t* p = reinterpret_cast<moveable_t*>(buffer_moveable);

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

      std::array<moveable_t, SIZE> src = {moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4),
                                          moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9)};

      auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, p, p + SIZE);

      bool is_equal = (output_moveable[0] == moveable_t(0)) && (output_moveable[1] == moveable_t(1)) && (output_moveable[2] == moveable_t(2))
                      && (output_moveable[3] == moveable_t(3)) && (output_moveable[4] == moveable_t(4)) && (output_moveable[5] == moveable_t(5))
                      && (output_moveable[6] == moveable_t(6)) && (output_moveable[7] == moveable_t(7)) && (output_moveable[8] == moveable_t(8))
                      && (output_moveable[9] == moveable_t(9));

      CHECK(is_equal);

      // Source elements should have been moved from (invalidated).
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(false, bool(src[i]));
      }

      CHECK(result.in == src.end());
      CHECK(result.out == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_n_output_shorter)
    {
      // Output range is shorter than count; should stop at output end.
      std::array<trivial_t, 5> small_dst = {};

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, small_dst.begin(), small_dst.end());

      bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.begin() + 5);
      CHECK(result.out == small_dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_n_count_shorter)
    {
      // Count is shorter than output range; should stop after count elements.
      trivial_t dst[SIZE] = {};

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move_n(src.begin(), 3, std::begin(dst), std::end(dst));

      bool is_equal = std::equal(std::begin(dst), std::begin(dst) + 3, test_data_trivial.begin());
      CHECK(is_equal);
      CHECK(result.in == src.begin() + 3);
      CHECK(result.out == std::begin(dst) + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_move_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::array<trivial_t, SIZE> src(test_data_trivial);

      auto result = etl::ranges::uninitialized_move_n(src.begin(), 0, p, p + SIZE);

      CHECK(result.in == src.begin());
      CHECK(result.out == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      unsigned char snapshot[sizeof(buffer_trivial)];
      std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial));

      auto result = etl::ranges::uninitialized_default_construct(p, p + SIZE);

      CHECK(result == p + SIZE);
      // For trivial types default construction is a no-op; raw storage must be
      // unchanged.
      CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_iterator_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_default_construct(p, p + SIZE);

      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_range_trivial)
    {
      alignas(trivial_t) unsigned char buf[sizeof(trivial_t) * SIZE];
      std::fill(std::begin(buf), std::end(buf), 0xFFu);
      unsigned char snapshot[sizeof(buf)];
      std::memcpy(snapshot, buf, sizeof(buf));
      trivial_t*                 p = reinterpret_cast<trivial_t*>(buf);
      etl::span<trivial_t, SIZE> dst(p, SIZE);

      auto result = etl::ranges::uninitialized_default_construct(dst);

      // For trivial types, default construction is a no-op, but the
      // returned iterator must point past the last element.
      CHECK(result == dst.end());
      // Raw storage must be unchanged.
      CHECK(std::memcmp(buf, snapshot, sizeof(buf)) == 0);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_range_non_trivial)
    {
      alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE];
      non_trivial_t*                       p = reinterpret_cast<non_trivial_t*>(buffer);
      etl::span<non_trivial_t, SIZE>       dst(p, SIZE);

      auto result = etl::ranges::uninitialized_default_construct(dst);

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

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_default_construct(p, p);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      unsigned char snapshot[sizeof(buffer_trivial)];
      std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial));

      auto result = etl::ranges::uninitialized_default_construct_n(p, SIZE);

      CHECK(result == p + SIZE);
      // For trivial types default construction is a no-op; raw storage must be
      // unchanged.
      CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_n_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_default_construct_n(p, SIZE);

      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_n_partial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU);
      unsigned char snapshot[sizeof(buffer_trivial)];
      std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial));

      auto result = etl::ranges::uninitialized_default_construct_n(p, 3);

      CHECK(result == p + 3);
      // For trivial types default construction is a no-op; raw storage must be
      // unchanged.
      CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0);

      etl::destroy(p, p + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_default_construct_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_default_construct_n(p, 0);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu);

      auto result = etl::ranges::uninitialized_value_construct(p, p + SIZE);

      CHECK(result == p + SIZE);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(trivial_t(), p[i]);
      }

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_iterator_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_value_construct(p, p + SIZE);

      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_range_trivial)
    {
      alignas(trivial_t) unsigned char buf[sizeof(trivial_t) * SIZE];
      std::fill(std::begin(buf), std::end(buf), 0xFFu);
      trivial_t*                 p = reinterpret_cast<trivial_t*>(buf);
      etl::span<trivial_t, SIZE> dst(p, SIZE);

      auto result = etl::ranges::uninitialized_value_construct(dst);

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

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(trivial_t(), p[i]);
      }
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_range_non_trivial)
    {
      alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE];
      non_trivial_t*                       p = reinterpret_cast<non_trivial_t*>(buffer);
      etl::span<non_trivial_t, SIZE>       dst(p, SIZE);

      auto result = etl::ranges::uninitialized_value_construct(dst);

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

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_value_construct(p, p);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu);

      auto result = etl::ranges::uninitialized_value_construct_n(p, SIZE);

      CHECK(result == p + SIZE);

      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(trivial_t(), p[i]);
      }

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_n_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      auto result = etl::ranges::uninitialized_value_construct_n(p, SIZE);

      CHECK(result == p + SIZE);

      etl::destroy(p, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_n_partial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu);

      auto result = etl::ranges::uninitialized_value_construct_n(p, 3);

      CHECK(result == p + 3);

      for (size_t i = 0; i < 3; ++i)
      {
        CHECK_EQUAL(trivial_t(), p[i]);
      }

      etl::destroy(p, p + 3);
    }

    //*************************************************************************
    TEST(test_ranges_uninitialized_value_construct_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::uninitialized_value_construct_n(p, 0);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_construct_at_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      etl::ranges::construct_at(p, test_item_trivial);
      CHECK_EQUAL(test_item_trivial, *p);

      etl::destroy_at(p);
    }

    //*************************************************************************
    TEST(test_ranges_construct_at_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      etl::ranges::construct_at(p, test_item_non_trivial);
      CHECK_EQUAL(test_item_non_trivial, *p);

      etl::destroy_at(p);
    }

    //*************************************************************************
    TEST(test_ranges_construct_at_default)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      etl::ranges::construct_at(p);
      CHECK_EQUAL(trivial_t(), *p);

      etl::destroy_at(p);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_at_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      etl::construct_at(p, test_item_trivial);
      CHECK_EQUAL(test_item_trivial, *p);

      etl::ranges::destroy_at(p);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_at_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

      etl::construct_at(p, test_item_non_trivial);
      CHECK_EQUAL(test_item_non_trivial, *p);

      etl::ranges::destroy_at(p);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_iterator_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      etl::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p);

      auto result = etl::ranges::destroy(p, p + SIZE);

      CHECK(result == p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_iterator_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p);

      auto result = etl::ranges::destroy(p, p + SIZE);

      CHECK(result == p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_range_trivial)
    {
      std::array<trivial_t, SIZE> dst;
      std::copy(test_data_trivial.begin(), test_data_trivial.end(), dst.begin());

      auto result = etl::ranges::destroy(dst);

      CHECK(result == dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_destroy_range_non_trivial)
    {
      alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE];
      non_trivial_t*                       p = reinterpret_cast<non_trivial_t*>(buffer);
      etl::span<non_trivial_t, SIZE>       dst(p, SIZE);

      etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p);

      auto result = etl::ranges::destroy(dst);

      CHECK(result == dst.end());
    }

    //*************************************************************************
    TEST(test_ranges_destroy_empty)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::destroy(p, p);

      CHECK(result == p);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_n_trivial)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

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

      etl::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p);

      auto result = etl::ranges::destroy_n(p, SIZE);

      CHECK(result == p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_n_non_trivial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p);

      auto result = etl::ranges::destroy_n(p, SIZE);

      CHECK(result == p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_n_partial)
    {
      non_trivial_t* p = reinterpret_cast<non_trivial_t*>(buffer_non_trivial);

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

      etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p);

      auto result = etl::ranges::destroy_n(p, 3);

      CHECK(result == p + 3);

      // Clean up the rest
      etl::destroy(p + 3, p + SIZE);
    }

    //*************************************************************************
    TEST(test_ranges_destroy_n_zero_count)
    {
      trivial_t* p = reinterpret_cast<trivial_t*>(buffer_trivial);

      auto result = etl::ranges::destroy_n(p, 0);

      CHECK(result == p);
    }
#endif

#if ETL_USING_CPP11
    //*************************************************************************
    TEST(test_trivially_relocate_trivial)
    {
      alignas(trivial_t) unsigned char src_buffer[sizeof(trivial_t) * SIZE];
      alignas(trivial_t) unsigned char dst_buffer[sizeof(trivial_t) * SIZE];

      trivial_t* src = reinterpret_cast<trivial_t*>(src_buffer);
      trivial_t* dst = reinterpret_cast<trivial_t*>(dst_buffer);

      // Initialize source
      for (size_t i = 0; i < SIZE; ++i)
      {
        src[i] = test_data_trivial[i];
      }

      // Relocate
      trivial_t* result = etl::trivially_relocate(src, src + SIZE, dst);

      // Check result
      CHECK(result == dst + SIZE);

      // Check destination values
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_data_trivial[i], dst[i]);
      }
    }

    //*************************************************************************
    TEST(test_trivially_relocate_same_location)
    {
      alignas(trivial_t) unsigned char buffer[sizeof(trivial_t) * SIZE];
      trivial_t*                       p = reinterpret_cast<trivial_t*>(buffer);

      // Initialize
      for (size_t i = 0; i < SIZE; ++i)
      {
        p[i] = test_data_trivial[i];
      }

      // Relocate to same location should return last
      trivial_t* result = etl::trivially_relocate(p, p + SIZE, p);

      CHECK(result == p + SIZE);

      // Values should be unchanged
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_data_trivial[i], p[i]);
      }
    }

    //*************************************************************************
    TEST(test_trivially_relocate_empty_range)
    {
      alignas(trivial_t) unsigned char src_buffer[sizeof(trivial_t) * SIZE];
      alignas(trivial_t) unsigned char dst_buffer[sizeof(trivial_t) * SIZE];

      trivial_t* src = reinterpret_cast<trivial_t*>(src_buffer);
      trivial_t* dst = reinterpret_cast<trivial_t*>(dst_buffer);

      // Relocate empty range
      trivial_t* result = etl::trivially_relocate(src, src, dst);

      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_trivially_relocate_overlapping_forward)
    {
      alignas(trivial_t) unsigned char buffer[sizeof(trivial_t) * (SIZE + 2)];
      trivial_t*                       p = reinterpret_cast<trivial_t*>(buffer);

      // Initialize
      for (size_t i = 0; i < SIZE; ++i)
      {
        p[i] = test_data_trivial[i];
      }

      // Relocate forward (overlapping) - shift elements by 2
      trivial_t* result = etl::trivially_relocate(p, p + SIZE, p + 2);

      CHECK(result == p + SIZE + 2);

      // Check values
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_data_trivial[i], p[i + 2]);
      }
    }

    //*************************************************************************
    TEST(test_relocate_trivial)
    {
      alignas(trivial_t) unsigned char src_buffer[sizeof(trivial_t) * SIZE];
      alignas(trivial_t) unsigned char dst_buffer[sizeof(trivial_t) * SIZE];

      trivial_t* src = reinterpret_cast<trivial_t*>(src_buffer);
      trivial_t* dst = reinterpret_cast<trivial_t*>(dst_buffer);

      // Initialize source
      for (size_t i = 0; i < SIZE; ++i)
      {
        src[i] = test_data_trivial[i];
      }

      // Relocate
      trivial_t* result = etl::relocate(src, src + SIZE, dst);

      // Check result
      CHECK(result == dst + SIZE);

      // Check destination values
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_data_trivial[i], dst[i]);
      }
    }

    //*************************************************************************
    TEST(test_relocate_same_location)
    {
      alignas(trivial_t) unsigned char buffer[sizeof(trivial_t) * SIZE];
      trivial_t*                       p = reinterpret_cast<trivial_t*>(buffer);

      // Initialize
      for (size_t i = 0; i < SIZE; ++i)
      {
        p[i] = test_data_trivial[i];
      }

      // Relocate to same location should return last
      trivial_t* result = etl::relocate(p, p + SIZE, p);

      CHECK(result == p + SIZE);

      // Values should be unchanged
      for (size_t i = 0; i < SIZE; ++i)
      {
        CHECK_EQUAL(test_data_trivial[i], p[i]);
      }
    }

    //*************************************************************************
    TEST(test_relocate_empty_range)
    {
      alignas(trivial_t) unsigned char src_buffer[sizeof(trivial_t) * SIZE];
      alignas(trivial_t) unsigned char dst_buffer[sizeof(trivial_t) * SIZE];

      trivial_t* src = reinterpret_cast<trivial_t*>(src_buffer);
      trivial_t* dst = reinterpret_cast<trivial_t*>(dst_buffer);

      // Relocate empty range
      trivial_t* result = etl::relocate(src, src, dst);

      CHECK(result == dst);
    }

    //*************************************************************************
    TEST(test_relocate_non_trivial)
    {
      const size_t N = 5;

      alignas(relocatable_t) unsigned char src_buffer[sizeof(relocatable_t) * N];
      alignas(relocatable_t) unsigned char dst_buffer[sizeof(relocatable_t) * N];

      relocatable_t* src = reinterpret_cast<relocatable_t*>(src_buffer);
      relocatable_t* dst = reinterpret_cast<relocatable_t*>(dst_buffer);

      // Placement-new source objects
      for (size_t i = 0; i < N; ++i)
      {
        ::new (static_cast<void*>(src + i)) relocatable_t(static_cast<int>(i + 1));
      }

      relocatable_t::reset_counts();

      // Relocate (non-trivial path: move-construct into dst, destroy src)
      relocatable_t* result = etl::relocate(src, src + N, dst);

      // Returned pointer must be one-past-end of destination
      CHECK(result == dst + N);

      // Destination objects were move-constructed with correct values
      for (size_t i = 0; i < N; ++i)
      {
        CHECK_EQUAL(static_cast<int>(i + 1), dst[i].value);
        CHECK(dst[i].was_moved_into);
      }

      // Destructors were called for the N source objects
      CHECK_EQUAL(static_cast<int>(N), relocatable_t::destructor_count);

      // Clean up destination objects
      relocatable_t::reset_counts();
      for (size_t i = 0; i < N; ++i)
      {
        dst[i].~relocatable_t();
      }
    }

    //*************************************************************************
    TEST(test_relocate_non_trivial_same_location)
    {
      const size_t N = 5;

      alignas(relocatable_t) unsigned char buffer[sizeof(relocatable_t) * N];
      relocatable_t*                       p = reinterpret_cast<relocatable_t*>(buffer);

      // Placement-new objects
      for (size_t i = 0; i < N; ++i)
      {
        ::new (static_cast<void*>(p + i)) relocatable_t(static_cast<int>(i + 1));
      }

      relocatable_t::reset_counts();

      // Relocating to the same location should be a no-op (early return)
      relocatable_t* result = etl::relocate(p, p + N, p);

      CHECK(result == p + N);

      // No destructors should have been called (no move-and-destroy performed)
      CHECK_EQUAL(0, relocatable_t::destructor_count);

      // Values should be unchanged
      for (size_t i = 0; i < N; ++i)
      {
        CHECK_EQUAL(static_cast<int>(i + 1), p[i].value);
        CHECK(!p[i].was_moved_into);
      }

      // Clean up
      relocatable_t::reset_counts();
      for (size_t i = 0; i < N; ++i)
      {
        p[i].~relocatable_t();
      }
    }

    //*************************************************************************
    TEST(test_relocate_non_trivial_empty_range)
    {
      alignas(relocatable_t) unsigned char src_buffer[sizeof(relocatable_t)];
      alignas(relocatable_t) unsigned char dst_buffer[sizeof(relocatable_t)];

      relocatable_t* src = reinterpret_cast<relocatable_t*>(src_buffer);
      relocatable_t* dst = reinterpret_cast<relocatable_t*>(dst_buffer);

      relocatable_t::reset_counts();

      // Empty range: first == last
      relocatable_t* result = etl::relocate(src, src, dst);

      CHECK(result == dst);

      // No destructors should have been called
      CHECK_EQUAL(0, relocatable_t::destructor_count);
    }
#endif

    //*************************************************************************
    TEST(test_wipe_on_destruct)
    {
      struct Data : public etl::wipe_on_destruct<Data>
      {
        uint32_t d1;
        uint32_t d2;
        char     d3;
      };

      alignas(Data) unsigned char buffer[sizeof(Data)] = {0};

      // Construct a Data object in the buffer with known non-zero values.
      Data* p = new (buffer) Data();
      p->d1   = 0x12345678UL;
      p->d2   = 0xAABBCCDDUL;
      p->d3   = char(0xEE);

      // Destroy the object; wipe_on_destruct should zero the memory.
      p->~Data();

      // Verify the memory occupied by the object has been cleared.
      unsigned char zeroes[sizeof(Data)] = {0};
      CHECK(memcmp(buffer, zeroes, sizeof(Data)) == 0);
    }

#if ETL_USING_CPP14
    //*************************************************************************
    TEST(test_default_delete_constexpr_copy_ctor)
    {
      constexpr etl::default_delete<int> d1;
      constexpr etl::default_delete<int> d2(d1);
      (void)d2;
      static_assert(sizeof(d2) > 0, "constexpr default_delete copy ctor");
      CHECK(true);
    }
#endif
  }
} // namespace
