diff --git a/README.rst b/README.rst index 5a1ceb9..b2e1515 100644 --- a/README.rst +++ b/README.rst @@ -9,9 +9,10 @@ Header-only library that provides 0 cost initialization for immutable containers Frozen provides: - immutable (a.k.a. frozen), ``constexpr``-compatible versions of ``std::set``, - ``std::unordered_set``, ``std::map`` and ``std::unordered_map``. - -- fixed-capacity, ``constinit``-compatible versions of ``std::map`` and + ``std::unordered_set``, ``std::map`` and ``std::unordered_map``, + ``std::vector``, and ``std::array``. + +- fixed-capacity, ``constinit``-compatible versions of ``std::map`` and ``std::unordered_map`` with immutable, compile-time selected keys mapped to mutable values. diff --git a/include/frozen/CMakeLists.txt b/include/frozen/CMakeLists.txt index 185378d..8f0855b 100644 --- a/include/frozen/CMakeLists.txt +++ b/include/frozen/CMakeLists.txt @@ -1,11 +1,13 @@ target_sources(frozen-headers INTERFACE "${prefix}/frozen/algorithm.h" + "${prefix}/frozen/array.h" "${prefix}/frozen/map.h" "${prefix}/frozen/random.h" "${prefix}/frozen/set.h" "${prefix}/frozen/string.h" "${prefix}/frozen/unordered_map.h" "${prefix}/frozen/unordered_set.h" + "${prefix}/frozen/vector.h" "${prefix}/frozen/bits/algorithms.h" "${prefix}/frozen/bits/basic_types.h" "${prefix}/frozen/bits/elsa.h" diff --git a/include/frozen/array.h b/include/frozen/array.h new file mode 100644 index 0000000..983f7d3 --- /dev/null +++ b/include/frozen/array.h @@ -0,0 +1,35 @@ +/* + * Frozen + * Copyright 2016 QuarksLab + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef FROZEN_LETITGO_ARRAY_H +#define FROZEN_LETITGO_ARRAY_H + +#include "frozen/bits/basic_types.h" + +namespace frozen { + +template +using array = bits::carray; + +} // namespace frozen + +#endif diff --git a/include/frozen/bits/basic_types.h b/include/frozen/bits/basic_types.h index b54a60d..112fbcb 100644 --- a/include/frozen/bits/basic_types.h +++ b/include/frozen/bits/basic_types.h @@ -39,8 +39,15 @@ struct ignored_arg {}; template class cvector { - T data [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise - std::size_t dsize = 0; + T data_[N] = {}; // zero-initialization for scalar type T, default-initialized otherwise + std::size_t dsize_ = 0; + + template + constexpr cvector(Iter iter, size_t size) + : dsize_(size) { + for (std::size_t i = 0; i < size; ++i) + data_[i] = *iter++; + } public: // Container typdefs @@ -51,36 +58,283 @@ class cvector { using const_pointer = const value_type *; using iterator = pointer; using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; // Constructors constexpr cvector(void) = default; - constexpr cvector(size_type count, const T& value) : dsize(count) { + constexpr cvector(size_type count, const T& value) : dsize_(count) { for (std::size_t i = 0; i < N; ++i) - data[i] = value; + data_[i] = value; } + template + constexpr cvector(T const (&init)[M]) + : cvector(init, M) + { + static_assert(M <= N, "Cannot initialize a cvector with a larger array"); + } + + constexpr cvector(std::initializer_list init) + : cvector(init.begin(), init.size()) {} + // Iterators - constexpr iterator begin() noexcept { return data; } - constexpr iterator end() noexcept { return data + dsize; } + constexpr iterator begin() noexcept { return data_; } + constexpr const_iterator begin() const noexcept { return data_; } + constexpr const_iterator cbegin() const noexcept { return data_; } + constexpr iterator end() noexcept { return data_ + dsize_; } + constexpr const_iterator end() const noexcept { return data_ + dsize_; } + constexpr const_iterator cend() const noexcept { return data_ + dsize_; } + + constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } + constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } // Capacity - constexpr size_type size() const { return dsize; } + constexpr bool empty() const { return dsize_ == 0; } + constexpr size_type size() const { return dsize_; } + constexpr size_type max_size() const { return N; } + constexpr void reserve(size_type capacity) { + if (capacity > N) { + FROZEN_THROW_OR_ABORT(std::length_error("Requested capacity (" + std::to_string(capacity) + ") too large (max " + std::to_string(N) + ')')); + } + } + constexpr size_type capacity() const { return N; } + constexpr void shrink_to_fit() { /* no-op */ } // Element access - constexpr reference operator[](std::size_t index) { return data[index]; } - constexpr const_reference operator[](std::size_t index) const { return data[index]; } + constexpr reference operator[](std::size_t index) { return data_[index]; } + constexpr const_reference operator[](std::size_t index) const { return data_[index]; } + + constexpr reference at(std::size_t index) { + if (index > dsize_) + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(dsize_) + ')')); + return data_[index]; + } + constexpr const_reference at(std::size_t index) const { + if (index > dsize_) + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(dsize_) + ')')); + return data_[index]; + } + + constexpr reference front() { return data_[0]; } + constexpr const_reference front() const { return data_[0]; } + + constexpr reference back() { return data_[dsize_ - 1]; } + constexpr const_reference back() const { return data_[dsize_ - 1]; } - constexpr reference back() { return data[dsize - 1]; } - constexpr const_reference back() const { return data[dsize - 1]; } + constexpr value_type* data() noexcept { return data; } + constexpr const value_type* data() const noexcept { return data; } // Modifiers - constexpr void push_back(const T & a) { data[dsize++] = a; } - constexpr void push_back(T && a) { data[dsize++] = std::move(a); } - constexpr void pop_back() { --dsize; } + constexpr void push_back(const T & a) { data_[dsize_++] = a; } + constexpr void push_back(T && a) { data_[dsize_++] = std::move(a); } + constexpr void pop_back() { --dsize_; } - constexpr void clear() { dsize = 0; } + constexpr void clear() { dsize_ = 0; } + + constexpr iterator insert(const_iterator pos, const_reference value) { + return insert(pos, 1, value); + } + + constexpr iterator insert(const_iterator pos, size_type count, const_reference value) { + if (pos > end()) { + FROZEN_THROW_OR_ABORT(std::out_of_range("Insertion postion out of bounds")); + } + + size_type remaining = capacity() - size(); + if (count > remaining) { + FROZEN_THROW_OR_ABORT(std::length_error("Remaining capacity (" + std::to_string(remaining) + ") smaller than requested length (" + std::to_string(count) + ")")); + } + + // Shift the existing values. + iterator start = const_cast(pos); + size_type num_to_shift = end() - start; + for (iterator src = start + (num_to_shift - 1), dest = src + count; + src >= start; + --src, --dest) { + *dest = *src; + } + + // Now insert the new ones. + dsize_ += count; + iterator dest = start; + while (count-- > 0) { + *(dest++) = value; + } + + return start; + } + + template + constexpr iterator insert(const_iterator pos, InputIterator first, InputIterator last) { + if (pos > end()) { + FROZEN_THROW_OR_ABORT(std::out_of_range("Insertion postion out of bounds")); + } + + size_type count = std::distance(first, last); + size_type remaining = capacity() - size(); + if (count > remaining) { + FROZEN_THROW_OR_ABORT(std::length_error("Remaining capacity (" + std::to_string(remaining) + ") smaller than requested length (" + std::to_string(count) + ")")); + } + + // Shift the existing values. + iterator start = const_cast(pos); + size_type num_to_shift = end() - start; + for (iterator src = start + (num_to_shift - 1), dest = src + count; + src >= start; + --src, --dest) { + *dest = *src; + } + + // Now insert the new ones. + dsize_ += count; + iterator dest = start; + for (InputIterator curr = first; curr != last; ++curr) { + *(dest++) = *curr; + } + + return start; + } + + constexpr iterator insert(const_iterator pos, std::initializer_list list) { + return insert(pos, list.begin(), list.end()); + } + + constexpr iterator erase(const_iterator pos) { + return erase(pos, pos + 1); + } + + constexpr iterator erase(const_iterator first, const_iterator last) { + if (dsize_ == 0) { + return end(); + } + else if (first == last) { + return const_cast(last); + } + else { + if (last != end()) { + for (iterator curr = const_cast(first), next = const_cast(last); + next != end(); + ++curr, ++next) { + *curr = *next; + } + } + + size_type num_to_remove = last - first; + dsize_ -= num_to_remove; + return const_cast(first); + } + } + + constexpr void resize(size_type count) { + resize(count, T{}); + } + + constexpr void resize(size_type count, const_reference value) { + if (count > N) { + FROZEN_THROW_OR_ABORT(std::length_error("Requested size (" + std::to_string(count) + ") too large (max " + std::to_string(N) + ')')); + } + + for (size_type i = dsize_; i < count; ++i) { + data_[i] = value; + } + + dsize_ = count; + } + + constexpr void fill(const value_type& val) { + for (std::size_t i = 0; i < N; ++i) + { + data_[i] = val; + } + dsize_ = N; + } +}; + +// Specialization for a compile-time empty container. +template +class cvector { +public: + // Container typdefs + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + using pointer = value_type *; + using const_pointer = const value_type *; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + // Constructors + constexpr cvector(void) = default; + constexpr cvector(size_type count, const T& value) + { + // static_assert(count == 0, "Cannot initialize empty cvector"); + (void)count; + (void)value; + } + + template + constexpr cvector(T const (&init)[M]) + { + static_assert(M == 0, "Cannot initialize empty cvector"); + (void)init; + } + + constexpr cvector(std::initializer_list init) + { + static_assert(init.size() == 0, "Cannot initialize empty cvector"); + } + + // Iterators + constexpr iterator begin() noexcept { return nullptr; } + constexpr const_iterator begin() const noexcept { return nullptr; } + constexpr const_iterator cbegin() const noexcept { return nullptr; } + constexpr iterator end() noexcept { return nullptr; } + constexpr const_iterator end() const noexcept { return nullptr; } + constexpr const_iterator cend() const noexcept { return nullptr; } + + constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(); } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(); } + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(); } + constexpr reverse_iterator rend() noexcept { return reverse_iterator(); } + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(); } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(); } + + // Capacity + constexpr bool empty() const { return true; } + constexpr size_type size() const { return 0; } + constexpr size_type max_size() const { return 0; } + constexpr size_type capacity() const { return 0; } + + // Element access + constexpr reference at(std::size_t index) { + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (0)")); + } + constexpr const_reference at(std::size_t index) const { + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (0)")); + } + + constexpr value_type* data() noexcept { return nullptr; } + constexpr const value_type* data() const noexcept { return nullptr; } + + // Modifiers + constexpr void push_back(const T & a) { (void)a; } + constexpr void push_back(T && a) { (void)a; } + constexpr void pop_back() {} + + constexpr void clear() {} + + constexpr void fill(const value_type& val) { (void)val; } }; template @@ -145,6 +399,7 @@ class carray { constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } // Capacity + constexpr bool empty() const { return N == 0; } constexpr size_type size() const { return N; } constexpr size_type max_size() const { return N; } @@ -178,6 +433,8 @@ class carray { data_[i] = val; } }; + +// Specialization for a compile-time empty container. template class carray { @@ -197,6 +454,65 @@ class carray { // Constructors constexpr carray(void) = default; + constexpr carray(size_type count, const T& value) + { + // static_assert(count == 0, "Cannot initialize empty carray"); + (void)count; + (void)value; + } + + template + constexpr carray(T const (&init)[M]) + { + static_assert(M == 0, "Cannot initialize empty carray"); + (void)init; + } + + template + constexpr carray(std::array const &init) + { + static_assert(M == 0, "Cannot initialize empty carray"); + (void)init; + } + + constexpr carray(std::initializer_list init) + { + static_assert(init.size() == 0, "Cannot initialize empty carray"); + } + + // Iterators + constexpr iterator begin() noexcept { return nullptr; } + constexpr const_iterator begin() const noexcept { return nullptr; } + constexpr const_iterator cbegin() const noexcept { return nullptr; } + constexpr iterator end() noexcept { return nullptr; } + constexpr const_iterator end() const noexcept { return nullptr; } + constexpr const_iterator cend() const noexcept { return nullptr; } + + constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(); } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(); } + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(); } + constexpr reverse_iterator rend() noexcept { return reverse_iterator(); } + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(); } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(); } + + // Capacity + constexpr bool empty() const { return true; } + constexpr size_type size() const { return 0; } + constexpr size_type max_size() const { return 0; } + + // Element access + constexpr reference at(std::size_t index) { + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (0)")); + } + constexpr const_reference at(std::size_t index) const { + FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (0)")); + } + + constexpr value_type* data() noexcept { return nullptr; } + constexpr const value_type* data() const noexcept { return nullptr; } + + // Modifiers + constexpr void fill(const value_type& val) { (void)val; } }; diff --git a/include/frozen/vector.h b/include/frozen/vector.h new file mode 100644 index 0000000..69022af --- /dev/null +++ b/include/frozen/vector.h @@ -0,0 +1,35 @@ +/* + * Frozen + * Copyright 2016 QuarksLab + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef FROZEN_LETITGO_VECTOR_H +#define FROZEN_LETITGO_VECTOR_H + +#include "frozen/bits/basic_types.h" + +namespace frozen { + +template +using vector = bits::cvector; + +} // namespace frozen + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5e64734..8dc6d06 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(frozen.tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bench.hpp ${CMAKE_CURRENT_LIST_DIR}/catch.hpp ${CMAKE_CURRENT_LIST_DIR}/test_algorithms.cpp + ${CMAKE_CURRENT_LIST_DIR}/test_array.cpp ${CMAKE_CURRENT_LIST_DIR}/test_main.cpp ${CMAKE_CURRENT_LIST_DIR}/test_map.cpp ${CMAKE_CURRENT_LIST_DIR}/test_rand.cpp @@ -19,7 +20,8 @@ target_sources(frozen.tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_unordered_map.cpp ${CMAKE_CURRENT_LIST_DIR}/test_unordered_map_str.cpp ${CMAKE_CURRENT_LIST_DIR}/test_unordered_set.cpp - ${CMAKE_CURRENT_LIST_DIR}/test_unordered_str_set.cpp) + ${CMAKE_CURRENT_LIST_DIR}/test_unordered_str_set.cpp + ${CMAKE_CURRENT_LIST_DIR}/test_vector.cpp) string(CONCAT generator # msvc gives invalid integral overflow warning for unsigned type diff --git a/tests/Makefile b/tests/Makefile index b4f75be..94660e3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,4 @@ -SRCS=test_main.cpp test_rand.cpp test_set.cpp test_map.cpp test_unordered_set.cpp test_str_set.cpp test_unordered_str_set.cpp test_unordered_map.cpp test_unordered_map_str.cpp test_str.cpp test_algorithms.cpp +SRCS=test_main.cpp test_rand.cpp test_set.cpp test_map.cpp test_unordered_set.cpp test_str_set.cpp test_unordered_str_set.cpp test_unordered_map.cpp test_unordered_map_str.cpp test_str.cpp test_algorithms.cpp test_array.cpp test_vector.cpp TARGET=test_main CXXFLAGS=-O3 -Wall -std=c++14 -march=native -Wextra -W -Werror -Wshadow -fPIC @@ -25,6 +25,8 @@ test_rand.o: test_rand.cpp \ test_algorithms.o: test_algorithms.cpp \ ../include/frozen/bits/algorithms.h \ catch.hpp +test_array.o: test_array.cpp ../include/frozen/array.h \ + ../include/frozen/bits/basic_types.h catch.hpp test_map.o: test_map.cpp ../include/frozen/map.h \ ../include/frozen/bits/algorithms.h catch.hpp test_set.o: test_set.cpp ../include/frozen/set.h \ @@ -62,3 +64,5 @@ test_str.o: test_str.cpp \ ../include/frozen/bits/basic_types.h ../include/frozen/bits/elsa.h \ ../include/frozen/string.h ../include/frozen/algorithm.h \ catch.hpp +test_vector.o: test_vector.cpp ../include/frozen/vector.h \ + ../include/frozen/bits/basic_types.h catch.hpp diff --git a/tests/test_array.cpp b/tests/test_array.cpp new file mode 100644 index 0000000..fee727b --- /dev/null +++ b/tests/test_array.cpp @@ -0,0 +1,70 @@ +#include + +#include "bench.hpp" +#include "catch.hpp" + +TEST_CASE("empty frozen array", "[array]") { + constexpr frozen::array ze_array{}; + + constexpr auto empty = ze_array.empty(); + REQUIRE(empty); + + constexpr auto size = ze_array.size(); + REQUIRE(size == 0); + + constexpr auto max_size = ze_array.max_size(); + REQUIRE(max_size == 0); + + auto constexpr begin = ze_array.begin(), end = ze_array.end(); + REQUIRE(begin == end); + + auto constexpr cbegin = ze_array.cbegin(), cend = ze_array.cend(); + REQUIRE(cbegin == cend); + + std::for_each(ze_array.begin(), ze_array.end(), [](int) {}); + REQUIRE(std::distance(ze_array.rbegin(), ze_array.rend()) == 0); + REQUIRE(std::count(ze_array.crbegin(), ze_array.crend(), 3) == 0); +} + +TEST_CASE("singleton frozen array", "[array]") { + constexpr frozen::array ze_array{1}; + + constexpr auto empty = ze_array.empty(); + REQUIRE(!empty); + + constexpr auto size = ze_array.size(); + REQUIRE(size == 1); + + constexpr auto max_size = ze_array.max_size(); + REQUIRE(max_size == 1); + + const auto value_op = ze_array[0]; + REQUIRE(value_op == 1); + + const auto value_at = ze_array.at(0); + REQUIRE(value_at == 1); + + auto const begin = ze_array.begin(), end = ze_array.end(); + REQUIRE((begin + 1) == end); + + auto const cbegin = ze_array.cbegin(), cend = ze_array.cend(); + REQUIRE(cbegin == (cend - 1)); + + std::for_each(ze_array.begin(), ze_array.end(), [](int) {}); + REQUIRE(std::distance(ze_array.rbegin(), ze_array.rend()) == 1); + REQUIRE(std::count(ze_array.crbegin(), ze_array.crend(), 3) == 0); + REQUIRE(std::count(ze_array.crbegin(), ze_array.crend(), 1) == 1); +} + +TEST_CASE("array from array", "[array]") { + constexpr short data[4]{1, 2, 3, 4}; + constexpr frozen::array ze_array(data); + constexpr auto value = ze_array[2]; + REQUIRE(value == 3); +} + +TEST_CASE("array from initializer", "[array]") { + constexpr frozen::array ze_array({1, 2, 3, 4}); + constexpr auto value = ze_array[2]; + REQUIRE(value == 3); +} diff --git a/tests/test_vector.cpp b/tests/test_vector.cpp new file mode 100644 index 0000000..91dfaf9 --- /dev/null +++ b/tests/test_vector.cpp @@ -0,0 +1,239 @@ +#include + +#include "bench.hpp" +#include "catch.hpp" + +TEST_CASE("empty frozen vector", "[vector]") { + constexpr frozen::vector ze_vector{}; + + constexpr auto empty = ze_vector.empty(); + REQUIRE(empty); + + constexpr auto size = ze_vector.size(); + REQUIRE(size == 0); + + constexpr auto max_size = ze_vector.max_size(); + REQUIRE(max_size == 0); + + constexpr auto capacity = ze_vector.capacity(); + REQUIRE(capacity == 0); + + auto constexpr begin = ze_vector.begin(), end = ze_vector.end(); + REQUIRE(begin == end); + + auto constexpr cbegin = ze_vector.cbegin(), cend = ze_vector.cend(); + REQUIRE(cbegin == cend); + + std::for_each(ze_vector.begin(), ze_vector.end(), [](int) {}); + REQUIRE(std::distance(ze_vector.rbegin(), ze_vector.rend()) == 0); + REQUIRE(std::count(ze_vector.crbegin(), ze_vector.crend(), 3) == 0); +} + +TEST_CASE("singleton frozen vector", "[vector]") { + constexpr frozen::vector ze_vector{1}; + + constexpr auto empty = ze_vector.empty(); + REQUIRE(!empty); + + constexpr auto size = ze_vector.size(); + REQUIRE(size == 1); + + constexpr auto max_size = ze_vector.max_size(); + REQUIRE(max_size == 1); + + constexpr auto capacity = ze_vector.capacity(); + REQUIRE(capacity == 1); + + const auto value_op = ze_vector[0]; + REQUIRE(value_op == 1); + + const auto value_at = ze_vector.at(0); + REQUIRE(value_at == 1); + + auto const begin = ze_vector.begin(), end = ze_vector.end(); + REQUIRE((begin + 1) == end); + + auto const cbegin = ze_vector.cbegin(), cend = ze_vector.cend(); + REQUIRE(cbegin == (cend - 1)); + + std::for_each(ze_vector.begin(), ze_vector.end(), [](int) {}); + REQUIRE(std::distance(ze_vector.rbegin(), ze_vector.rend()) == 1); + REQUIRE(std::count(ze_vector.crbegin(), ze_vector.crend(), 3) == 0); + REQUIRE(std::count(ze_vector.crbegin(), ze_vector.crend(), 1) == 1); +} + +TEST_CASE("non-full frozen vector", "[vector]") { + constexpr frozen::vector ze_vector{1}; + + constexpr auto empty = ze_vector.empty(); + REQUIRE(!empty); + + constexpr auto size = ze_vector.size(); + REQUIRE(size == 1); + + constexpr auto max_size = ze_vector.max_size(); + REQUIRE(max_size == 4); + + constexpr auto capacity = ze_vector.capacity(); + REQUIRE(capacity == 4); + + const auto value_op = ze_vector[0]; + REQUIRE(value_op == 1); + + const auto value_at = ze_vector.at(0); + REQUIRE(value_at == 1); + + auto const begin = ze_vector.begin(), end = ze_vector.end(); + REQUIRE((begin + 1) == end); + + auto const cbegin = ze_vector.cbegin(), cend = ze_vector.cend(); + REQUIRE(cbegin == (cend - 1)); + + std::for_each(ze_vector.begin(), ze_vector.end(), [](int) {}); + REQUIRE(std::distance(ze_vector.rbegin(), ze_vector.rend()) == 1); + REQUIRE(std::count(ze_vector.crbegin(), ze_vector.crend(), 3) == 0); + REQUIRE(std::count(ze_vector.crbegin(), ze_vector.crend(), 1) == 1); +} + +TEST_CASE("vector from array", "[vector]") { + constexpr short data1[4]{1, 2, 3, 4}; + constexpr frozen::vector ze_vector1(data1); + + constexpr auto value1 = ze_vector1[2]; + REQUIRE(value1 == 3); + + constexpr short data2[3]{1, 2, 3}; + constexpr frozen::vector ze_vector2(data2); + + constexpr auto value2 = ze_vector2[2]; + REQUIRE(value2 == 3); +} + +TEST_CASE("vector from initializer", "[vector]") { + constexpr frozen::vector ze_vector1({1, 2, 3, 4}); + constexpr auto value1 = ze_vector1[2]; + REQUIRE(value1 == 3); + + constexpr frozen::vector ze_vector2({1, 2, 3}); + constexpr auto value2 = ze_vector2[2]; + REQUIRE(value2 == 3); +} + +TEST_CASE("vector insert single", "[vector]") { + frozen::vector ze_vector({1, 4}); + REQUIRE(ze_vector.size() == 2); + + auto iter = ze_vector.insert(ze_vector.begin() + 1, 2); + REQUIRE(ze_vector.size() == 3); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 2); + REQUIRE(ze_vector[2] == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector insert multi", "[vector]") { + frozen::vector ze_vector({1, 4}); + REQUIRE(ze_vector.size() == 2); + + auto iter = ze_vector.insert(ze_vector.begin() + 1, (size_t)2, 10); + REQUIRE(ze_vector.size() == 4); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 10); + REQUIRE(ze_vector[2] == 10); + REQUIRE(ze_vector[3] == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector insert range", "[vector]") { + frozen::vector ze_vector({1, 4}); + REQUIRE(ze_vector.size() == 2); + + constexpr frozen::vector new_values({2, 3}); + + auto iter = ze_vector.insert(ze_vector.begin() + 1, new_values.begin(), new_values.end()); + REQUIRE(ze_vector.size() == 4); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 2); + REQUIRE(ze_vector[2] == 3); + REQUIRE(ze_vector[3] == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector insert list", "[vector]") { + frozen::vector ze_vector({1, 4}); + REQUIRE(ze_vector.size() == 2); + + auto iter = ze_vector.insert(ze_vector.begin() + 1, {2, 3}); + REQUIRE(ze_vector.size() == 4); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 2); + REQUIRE(ze_vector[2] == 3); + REQUIRE(ze_vector[3] == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector erase single", "[vector]") { + frozen::vector ze_vector({1, 2, 3, 4}); + REQUIRE(ze_vector.size() == 4); + + auto iter = ze_vector.erase(ze_vector.begin() + 2); + REQUIRE(ze_vector.size() == 3); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 2); + REQUIRE(ze_vector[2] == 4); + REQUIRE(iter == ze_vector.begin() + 2); +} + +TEST_CASE("vector erase range", "[vector]") { + frozen::vector ze_vector({1, 2, 3, 4}); + REQUIRE(ze_vector.size() == 4); + + auto iter = ze_vector.erase(ze_vector.begin() + 1, ze_vector.begin() + 3); + REQUIRE(ze_vector.size() == 2); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector erase none", "[vector]") { + frozen::vector ze_vector({1, 2, 3, 4}); + REQUIRE(ze_vector.size() == 4); + + auto iter = ze_vector.erase(ze_vector.begin() + 1, ze_vector.begin() + 1); + REQUIRE(ze_vector.size() == 4); + REQUIRE(iter == ze_vector.begin() + 1); +} + +TEST_CASE("vector reserve", "[vector]") { + frozen::vector ze_vector({1, 2}); + REQUIRE(ze_vector.size() == 2); + REQUIRE(ze_vector.capacity() == 4); + + // All reserve operations should be no-op. + REQUIRE(ze_vector.size() == 2); + REQUIRE(ze_vector.capacity() == 4); +} + +TEST_CASE("vector resize", "[vector]") { + frozen::vector ze_vector({1, 2}); + REQUIRE(ze_vector.size() == 2); + + // Resize larger with default element value (0). + ze_vector.resize(3); + REQUIRE(ze_vector.size() == 3); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 2); + REQUIRE(ze_vector[2] == 0); + + // Resize smaller. + ze_vector.resize(1); + REQUIRE(ze_vector.size() == 1); + REQUIRE(ze_vector[0] == 1); + + // Resize larger with specified value. + ze_vector.resize(3, 6); + REQUIRE(ze_vector.size() == 3); + REQUIRE(ze_vector[0] == 1); + REQUIRE(ze_vector[1] == 6); + REQUIRE(ze_vector[2] == 6); +}