diff --git a/.codespellignore b/.codespellignore index 0f74e6f..46adf0f 100644 --- a/.codespellignore +++ b/.codespellignore @@ -1 +1,2 @@ claus +moint diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 50b03d9..8fe6056 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -31,13 +31,13 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-24.04] compiler: - cpp: g++ c: gcc - cpp: clang++ c: clang - cpp_version: [17, 20, 23, 26] + cpp_version: [23] cmake_args: - description: "Default" args: "" @@ -46,16 +46,22 @@ jobs: - description: "ASan" args: "-DCMAKE_CXX_FLAGS='-fsanitize=address -fsanitize=undefined'" include: - # Needs C++ 20 as C++17 selectivly disables ranges and concepts - # related functionalities - - platform: ubuntu-latest + - platform: ubuntu-24.04 compiler: cpp: g++ c: gcc - cpp_version: 20 + cpp_version: 23 cmake_args: description: "Werror" args: "-DCMAKE_CXX_FLAGS='-Wall -Wextra -Wpedantic -Werror'" + - platform: ubuntu-24.04 + compiler: + cpp: g++ + c: gcc + cpp_version: 23 + cmake_args: + description: "Dynamic" + cmake_args: "-DBUILD_SHARED_LIBS=on" name: "Build & Test: ${{ matrix.compiler.c }} ${{ matrix.cpp_version }} ${{ matrix.cmake_args.description }}" runs-on: ${{ matrix.platform }} @@ -66,6 +72,22 @@ jobs: with: cmakeVersion: "~3.25.0" ninjaVersion: "^1.11.1" + - name: Install latest compiler + run: | + if [ "${{ matrix.compiler.cpp}}" == "g++" ]; then + # Install gcc-14 + sudo apt-get update + sudo apt-get install -y gcc-14 g++-14 + + sudo rm /usr/bin/gcc + sudo ln -s /usr/bin/gcc-14 /usr/bin/gcc + + sudo rm /usr/bin/g++ + sudo ln -s /usr/bin/g++-14 /usr/bin/g++ + else + # Install llvm + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + fi - name: Print installed software run: | clang++ --version diff --git a/README.md b/README.md index e10ce1f..f1936fd 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ which dynamic memory allocations are undesired. #include -using namespace beman::inplace_vector; +using namespace beman; /** * Generates fibonacci sequence using inplace_vector. @@ -56,7 +56,7 @@ template inplace_vector fibonacci_to(int num) { ### Compiler support -Building this repository requires **C++17** or later. +Building this repository requires **C++23** or later. ### Dependencies diff --git a/examples/fibonacci.cpp b/examples/fibonacci.cpp index 2f1b330..0c77ceb 100644 --- a/examples/fibonacci.cpp +++ b/examples/fibonacci.cpp @@ -4,7 +4,7 @@ #include -using namespace beman::inplace_vector; +using namespace beman; /** * Generates fibonacci sequence using inplace_vector. diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 75dd172..7aa037f 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -1,793 +1,1034 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cpp_concepts -#include -#endif - -#ifdef __cpp_lib_containers_ranges +#pragma once +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* + * SPDX-FileCopyrightText: Copyright (c) 2023 Gonzalo Brito Gadeschi. All rights +reserved. + * SPDX-License-Identifier: MIT + * SPDX-License-Identifier: Apache 2.0 with LLVM exception + * That is, this project is Dual Licensed. + * + * MIT LICENSE: + * + * 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 Precondition, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * APACHE 2.0 with LLVM Exception: + ============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + */ +#include // for rotate, equals, move_backwards, ... +#include +#include // for lots... +#include // for size_t +#include // for fixed-width integer types +#include // for less and equal_to +#include // for reverse_iterator and iterator traits +#include // for numeric_limits +#include // for operator new #include +#include // for length_error +#include // for assertion diagnostics +#include // for aligned_storage and all meta-functions + +// Optimizer allowed to assume that EXPR evaluates to true +#define __IV_ASSUME(__EXPR) \ + static_cast((__EXPR) ? void(0) : __builtin_unreachable) + +// Assert pretty printer +#define __IV_ASSERT(...) \ + static_cast((__VA_ARGS__) \ + ? void(0) \ + : ::beman::__iv_detail::__assert_failure( \ + static_cast(__FILE__), __LINE__, \ + "assertion failed: " #__VA_ARGS__)) + +// Assert in debug, assume in release. +// #ifdef NDEBUG +// #define __IV_EXPECT(__EXPR) __IV_ASSUME(__EXPR) +// #else +// #define __IV_EXPECT(__EXPR) __IV_ASSERT(__EXPR) +// #endif + +// TODO River: Disabled temporarily +#define __IV_EXPECT(__EXPR) + +/* +// BUGBUG workaround for libstdc++ not providing from_range_t / from_range yet +namespace std { +#if defined(__GLIBCXX__) || defined(__GLIBCPP__) +struct from_range_t {}; +inline constexpr from_range_t from_range; #endif +}; // namespace std +*/ + +// TODO(River): using namespace std below, we need to specify +// beman::from_range_t +namespace beman { +struct from_range_t {}; +inline constexpr from_range_t from_range; +}; // namespace beman + +// Private utilities +namespace beman::__iv_detail { + +template +[[noreturn]] +static constexpr void __assert_failure(char const *__file, int __line, + char const *__msg) { + if consteval { + throw __msg; // TODO: std lib implementer, do better here + } else { + fprintf(stderr, "%s(%d): %s\n", __file, __line, __msg); + abort(); + } +} + +using namespace std; +using namespace beman::__iv_detail; + +// clang-format off +// Smallest unsigned integer that can represent values in [0, N]. +template +using __smallest_size_t += conditional_t<(__N < numeric_limits::max()), uint8_t, + conditional_t<(__N < numeric_limits::max()), uint16_t, + conditional_t<(__N < numeric_limits::max()), uint32_t, + conditional_t<(__N < numeric_limits::max()), uint64_t, + size_t>>>>; +// clang-format on + +// Index a random-access and sized range doing bound checks in debug builds +template +static constexpr decltype(auto) __index(__Rng &&__rng, __Index __i) noexcept + requires(ranges::sized_range<__Rng>) +{ + __IV_EXPECT(static_cast(__i) < ranges::size(__rng)); + return begin(::std::forward<__Rng>(__rng))[::std::forward<__Index>(__i)]; +} + +// http://eel.is/c++draft/container.requirements.general#container.intro.reqmts-2 +template +concept __container_compatible_range = + ranges::input_range<__Rng> && + convertible_to, __T>; + +template +concept __move_or_copy_insertable_from = requires(__Ptr __ptr, __T &&__value) { + { construct_at(__ptr, ::std::forward<__T &&>(__value)) } -> same_as<__Ptr>; +}; -#ifdef __cpp_lib_three_way_comparison -#include -#endif +} // namespace beman::__iv_detail -namespace beman::inplace_vector { -#ifdef __cpp_concepts -namespace detail { -// Exposition-only container-compatible-range -template -concept container_compatible_range_impl = requires(T &&t) { - { - std::ranges::begin(t) - } -> std::same_as::iterator>; - { - std::ranges::end(t) - } -> std::same_as::iterator>; +// Types implementing the `inplace_vector`'s storage +namespace beman::__iv_detail::__storage { + +// TODO: flesh out +template struct __aligned_storage2 { + alignas(__T) byte __d[sizeof(__T) * __N]; + constexpr __T *__data(size_t __i) noexcept { + __IV_EXPECT(__i < __N); + return reinterpret_cast<__T *>(__d) + __i; + } + constexpr const __T *__data(size_t __i) const noexcept { + __IV_EXPECT(__i < __N); + return reinterpret_cast(__d) + __i; + } }; -} // namespace detail -template -constexpr bool container_compatible_range = - detail::container_compatible_range_impl; -#endif -template -using If = typename std::conditional::type; - -// The internal size type can be smaller than std::size_t when capacity allows -// for it. -template -using inplace_vector_internal_size_type = - If::max(), uint8_t, - If::max(), uint16_t, - If::max(), uint32_t, - uint64_t>>>; - -// Base class for inplace_vector -template -struct inplace_vector_destruct_base { - using size_type = std::size_t; - using internal_size_type = inplace_vector_internal_size_type; - - alignas(T) unsigned char elems[Capacity * sizeof(T)] = {}; - internal_size_type size_{0}; - - // [containers.sequences.inplace.vector.cons], construct/copy/destroy - constexpr inplace_vector_destruct_base() = default; - - inplace_vector_destruct_base( - const inplace_vector_destruct_base - &other) noexcept(std::is_nothrow_copy_constructible_v) - : elems(), size_(other.size_) {} - - inplace_vector_destruct_base( - const inplace_vector_destruct_base - &&other) noexcept(std::is_nothrow_move_constructible_v) - : elems(), size_(other.size_) {} - - inplace_vector_destruct_base & - operator=(const inplace_vector_destruct_base &other) noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_assignable_v) { - size_ = other.size_; - } - - inplace_vector_destruct_base & - operator=(const inplace_vector_destruct_base &&other) noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_assignable_v) { - size_ = other.size_; - other.size_ = nullptr; - } - - constexpr inplace_vector_destruct_base(const size_type size) - : elems(), size_(size) { - if (Capacity < size) { - throw std::bad_alloc(); - } +// Storage for zero elements. +template struct __zero_sized { +protected: + using __size_type = uint8_t; + static constexpr __T *__data() noexcept { return nullptr; } + static constexpr __size_type __size() noexcept { return 0; } + static constexpr void __unsafe_set_size(size_t __new_size) noexcept { + __IV_EXPECT(__new_size == 0 && + "tried to change size of empty storage to non-zero value"); } + +public: + constexpr __zero_sized() = default; + constexpr __zero_sized(__zero_sized const &) = default; + constexpr __zero_sized &operator=(__zero_sized const &) = default; + constexpr __zero_sized(__zero_sized &&) = default; + constexpr __zero_sized &operator=(__zero_sized &&) = default; + constexpr ~__zero_sized() = default; }; -template -struct inplace_vector_base : public inplace_vector_destruct_base { - using size_type = std::size_t; - using reference = T &; - using iterator = T *; - using const_iterator = const T *; - - // [containers.sequences.inplace.vector.cons], construct/copy/destroy - constexpr inplace_vector_base() = default; - inplace_vector_base(const inplace_vector_base &other) noexcept( - std::is_nothrow_copy_constructible_v) - : inplace_vector_destruct_base(other.size) { - std::copy(other.begin(), other.end(), begin()); - } - inplace_vector_base(inplace_vector_base &&other) noexcept( - Capacity == 0 || std::is_nothrow_move_constructible_v) - : inplace_vector_destruct_base(other.size_) { - std::copy(other.begin(), other.end(), begin()); - std::destroy(other.begin(), other.end()); - other.size_ = 0; - } - inplace_vector_base &operator=(const inplace_vector_base &other) noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_assignable_v) { - const auto diff = static_cast(other.size() - size()); - // other.size is less than size - if (diff < 0) { - const auto new_end = std::copy(other.begin(), other.end(), begin()); - // destroy unnecessary memory - std::destroy(new_end, end()); - } - // other.size is greater than size - else { - // copy other vector into the current vector until it runs ouf of size - std::copy(other.begin(), other.begin() + size(), begin()); - // copy the other half after the end of the current vector - std::copy(other.begin() + size(), other.end(), end()); - } - this->size_ = other.size(); - return *this; +// Storage for trivial types. +template struct __trivial { + static_assert(is_trivial_v<__T>, + "storage::trivial requires Trivial"); + static_assert(__N != size_t{0}, "__N == 0, use __zero_sized"); + +protected: + using __size_type = __smallest_size_t<__N>; + +private: + // If value_type is const, then const array of non-const elements: + using __data_t = conditional_t, array<__T, __N>, + const array, __N>>; + alignas(alignof(__T)) __data_t __data_{}; + __size_type __size_ = 0; + +protected: + constexpr const __T *__data() const noexcept { return __data_.data(); } + constexpr __T *__data() noexcept { return __data_.data(); } + constexpr __size_type __size() const noexcept { return __size_; } + constexpr void __unsafe_set_size(size_t __new_size) noexcept { + __IV_EXPECT(__size_type(__new_size) <= __N && + "new_size out-of-bounds [0, N]"); + __size_ = __size_type(__new_size); } - inplace_vector_base &operator=(inplace_vector_base &&other) noexcept( - Capacity == 0 || (std::is_nothrow_move_constructible_v && - std::is_nothrow_move_assignable_v)) { - const auto diff = static_cast(other.size() - size()); - // other size is less than size - if (diff < 0) { - const auto new_end = std::move(other.begin(), other.end(), begin()); - std::destroy(new_end, end()); - // other size is grater than size - } else { - std::move(other, other.begin(), other.begin() + size(), begin()); - std::move(other.begin() + size(), other.end(), end()); - } - this->size_ = other.size(); - std::destroy(other.begin(), other.end()); - // reset size to zero - other.change_size(-static_cast(other.size())); - return *this; +public: + constexpr __trivial() noexcept = default; + constexpr __trivial(__trivial const &) noexcept = default; + constexpr __trivial &operator=(__trivial const &) noexcept = default; + constexpr __trivial(__trivial &&) noexcept = default; + constexpr __trivial &operator=(__trivial &&) noexcept = default; + constexpr ~__trivial() = default; +}; + +/// Storage for non-trivial elements. +template struct __non_trivial { + static_assert(!is_trivial_v<__T>, + "use storage::trivial for Trivial elements"); + static_assert(__N != size_t{0}, "use storage::zero for __N==0"); + +protected: + using __size_type = __smallest_size_t<__N>; + +private: + using __data_t = + conditional_t, __aligned_storage2<__T, __N>, + const __aligned_storage2, __N>>; + __data_t __data_{}; // BUGBUG: test SIMD types + __size_type __size_ = 0; + +protected: + constexpr const __T *__data() const noexcept { return __data_.__data(0); } + constexpr __T *__data() noexcept { return __data_.__data(0); } + constexpr __size_type __size() const noexcept { return __size_; } + constexpr void __unsafe_set_size(size_t __new_size) noexcept { + __IV_EXPECT(__size_type(__new_size) <= __N && + "new_size out-of-bounds [0, __N)"); + __size_ = __size_type(__new_size); } - constexpr inplace_vector_base(const size_type size) - : inplace_vector_destruct_base(size) {} - // [containers.sequences.inplace.vector.members] size/capacity - constexpr void change_size(const std::ptrdiff_t count) noexcept { - this->size_ += count; +public: + constexpr __non_trivial() noexcept = default; + constexpr __non_trivial(__non_trivial const &) noexcept = default; + constexpr __non_trivial &operator=(__non_trivial const &) noexcept = default; + constexpr __non_trivial(__non_trivial &&) noexcept = default; + constexpr __non_trivial &operator=(__non_trivial &&) noexcept = default; + constexpr ~__non_trivial() = default; +}; + +// Selects the vector storage. +template +using _t = conditional_t<__N == 0, __zero_sized<__T>, + conditional_t, __trivial<__T, __N>, + __non_trivial<__T, __N>>>; + +} // namespace beman::__iv_detail::__storage + +namespace beman { + +using namespace std; + +/// Dynamically-resizable fixed-__N vector with inplace storage. +template +struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { +private: + static_assert(is_nothrow_destructible_v<__T>, + "T must be nothrow destructible"); + using __base_t = __iv_detail::__storage::_t<__T, __N>; + using __self = inplace_vector<__T, __N>; + using __base_t::__data; + using __base_t::__size; + using __base_t::__unsafe_set_size; + +public: + using value_type = __T; + using pointer = __T *; + using const_pointer = const __T *; + using reference = value_type &; + using const_reference = const value_type &; + using size_type = size_t; + using difference_type = ptrdiff_t; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = ::std::reverse_iterator; + using const_reverse_iterator = ::std::reverse_iterator; + + // [containers.sequences.inplace_vector.cons], construct/copy/destroy + constexpr inplace_vector() noexcept { __unsafe_set_size(0); } + // constexpr explicit inplace_vector(size_type __n); + // constexpr inplace_vector(size_type __n, const __T& __value); + // template // BUGBUG: why not model input_iterator? + // constexpr inplace_vector(__InputIterator __first, __InputIterator + // __last); + // template <__iv_detail::__container_compatible_range<__T> __R> + // constexpr inplace_vector(from_range_t, __R&& __rg); + // from base-class, trivial if is_trivially_copy_constructible_v: + // constexpr inplace_vector(const inplace_vector&); + // from base-class, trivial if is_trivially_move_constructible_v + // constexpr inplace_vector(inplace_vector&&) noexcept(__N == 0 || + // is_nothrow_move_constructible_v<__T>); + // constexpr inplace_vector(initializer_list<__T> __il); + // from base-class, trivial if is_trivially_destructible_v<__T> + // constexpr ~inplace_vector(); + // from base-class, trivial if is_trivially_destructible_v<__T> && + // is_trivially_copy_assignable_v<__T> + // constexpr inplace_vector& operator=(const inplace_vector& __other); + // from base-class, trivial if is_trivially_destructible_v<__T> && + // is_trivially_copy_assignable_v<__T> + // constexpr inplace_vector& operator=(inplace_vector&& __other) + // noexcept(__N == 0 || is_nothrow_move_assignable_v<__T>); + // template // BUGBUG: why not model input_iterator + // constexpr void assign(__InputIterator __first, __InputIterator l__ast); + // template<__iv_detail::__container_compatible_range<__T> __R> + // constexpr void assign_range(__R&& __rg); + // constexpr void assign(size_type __n, const __T& __u); + // constexpr void assign(initializer_list<__T> __il); + + // iterators + constexpr iterator begin() noexcept { return __data(); } + constexpr const_iterator begin() const noexcept { return __data(); } + constexpr iterator end() noexcept { return begin() + size(); } + constexpr const_iterator end() const noexcept { return begin() + size(); } + constexpr reverse_iterator rbegin() noexcept { + return reverse_iterator(end()); } - constexpr size_type size() const noexcept { - return static_cast(this->size_); + constexpr const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); } - constexpr bool empty() const noexcept { return this->size_ == 0; } - - // [containers.sequences.inplace.vector.data], data access - constexpr T *data() noexcept { - return std::launder(reinterpret_cast(this->elems)); + constexpr reverse_iterator rend() noexcept { + return reverse_iterator(begin()); } - constexpr const T *data() const noexcept { - return std::launder(reinterpret_cast(this->elems)); + constexpr const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); } - // [containers.sequences.inplace.vector.iterators] iterators - iterator begin() noexcept { - return std::launder(reinterpret_cast(this->elems)); + constexpr const_iterator cbegin() const noexcept { return __data(); } + constexpr const_iterator cend() const noexcept { return cbegin() + size(); } + constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(cend()); } - const_iterator begin() const noexcept { - return std::launder(reinterpret_cast(this->elems)); + constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(cbegin()); } - iterator end() noexcept { - return std::launder(reinterpret_cast(this->elems) + this->size()); + [[nodiscard]] constexpr bool empty() const noexcept { return __size() == 0; }; + constexpr size_type size() const noexcept { return __size(); } + static constexpr size_type max_size() noexcept { return __N; } + static constexpr size_type capacity() noexcept { return __N; } + // constexpr void resize(size_type __sz); + // constexpr void resize(size_type __sz, const __T& __c); + constexpr void reserve(size_type __n) { + if (__n > __N) [[unlikely]] + throw bad_alloc(); } + constexpr void shrink_to_fit() {} - const_iterator end() const noexcept { - return std::launder(reinterpret_cast(this->elems) + - this->size()); + // element access + constexpr reference operator[](size_type __n) { + return __iv_detail::__index(*this, __n); } - - // [containers.sequences.inplace.vector.modifiers], modifiers - static constexpr void uninitialized_value_construct(iterator first, - iterator last) noexcept { - std::fill(first, last, T()); + constexpr const_reference operator[](size_type __n) const { + return __iv_detail::__index(*this, __n); } - - static constexpr void uninitialized_fill(iterator first, iterator last, - const T &value) noexcept { - std::fill(first, last, value); + // constexpr const_reference at(size_type __n) const; + // constexpr reference at(size_type __n); + constexpr reference front() { + return __iv_detail::__index(*this, size_type(0)); } - - template - static constexpr void uninitialized_copy(Iter first, Iter last, - iterator dest) noexcept { - std::copy(first, last, dest); + constexpr const_reference front() const { + return __iv_detail::__index(*this, size_type(0)); + } + constexpr reference back() { + return __iv_detail::__index(*this, size() - size_type(1)); + } + constexpr const_reference back() const { + return __iv_detail::__index(*this, size() - size_type(1)); } - template - constexpr void uninitialized_move(Iter first, Iter last, - iterator dest) noexcept { - std::move(first, last, dest); + // [containers.sequences.inplace_vector.data], data access + constexpr __T *data() noexcept { return __data(); } + constexpr const __T *data() const noexcept { return __data(); } + + // [containers.sequences.inplace_vector.modifiers], modifiers + // template + // constexpr __T& emplace_back(__Args&&... __args); + // constexpr __T& push_back(const __T& __x); + // constexpr __T& push_back(__T&& __x); + // template<__iv_detail::__container_compatible_range<__T> __R> + // constexpr void append_range(__R&& __rg); + // constexpr void pop_back(); + + // template + // constexpr __T* try_emplace_back(__Args&&... __args); + // constexpr __T* try_push_back(const __T& __value); + // constexpr __T* try_push_back(__T&& __value); + + // template + // constexpr __T& unchecked_emplace_back(__Args&&... __args); + // constexpr __T& unchecked_push_back(const __T& __value); + // constexpr __T& unchecked_push_back(__T&& __value); + + // template + // constexpr iterator emplace(const_iterator __position, __Args&&... __args); + // constexpr iterator insert(const_iterator __position, const __T& __x); + // constexpr iterator insert(const_iterator __position, __T&& __x); + // constexpr iterator insert(const_iterator __position, size_type __n, const + // __T& __x); + // template + // constexpr iterator insert(const_iterator __position, __InputIterator + // __first, __InputIterator __last); + // template<__iv_detail::__container_compatible_range<__T> __R> + // constexpr iterator insert_range(const_iterator __position, __R&& __rg); + // constexpr iterator insert(const_iterator __position, initializer_list<__T> + // __il); constexpr iterator erase(const_iterator __position); constexpr + // iterator erase(const_iterator __first, const_iterator __last); constexpr + // void swap(inplace_vector& __x) + // noexcept(__N == 0 || (is_nothrow_swappable_v<__T> && + // is_nothrow_move_constructible_v<__T>)); + // constexpr void clear() noexcept; + + constexpr friend bool operator==(const inplace_vector &__x, + const inplace_vector &__y) { + return __x.size() == __y.size() && ::std::ranges::equal(__x, __y); + } + // constexpr friend auto /*synth-three-way-result*/ + // operator<=>(const inplace_vector& __x, const inplace_vector& __y); + constexpr friend void swap(inplace_vector &__x, inplace_vector &__y) noexcept( + __N == 0 || + (is_nothrow_swappable_v<__T> && is_nothrow_move_constructible_v<__T>)) { + __x.swap(__y); } -}; -template -class inplace_vector : public inplace_vector_base { -private: - using base = inplace_vector_base; +private: // Utilities + constexpr void __assert_iterator_in_range(const_iterator __it) noexcept { + __IV_EXPECT(begin() <= __it && "iterator not in range"); + __IV_EXPECT(__it <= end() && "iterator not in range"); + } + constexpr void __assert_valid_iterator_pair(const_iterator __first, + const_iterator __last) noexcept { + __IV_EXPECT(__first <= __last && "invalid iterator pair"); + } + constexpr void + __assert_iterator_pair_in_range(const_iterator __first, + const_iterator __last) noexcept { + __assert_iterator_in_range(__first); + __assert_iterator_in_range(__last); + __assert_valid_iterator_pair(__first, __last); + } + constexpr void + __unsafe_destroy(__T *__first, + __T *__last) noexcept(is_nothrow_destructible_v<__T>) { + __assert_iterator_pair_in_range(__first, __last); + if constexpr (__N > 0 && !is_trivial_v<__T>) { + for (; __first != __last; ++__first) + __first->~__T(); + } + } public: - using value_type = T; - using pointer = T *; - using const_pointer = const T *; - using reference = value_type &; - using const_reference = const value_type &; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; + // Implementation - // [containers.sequences.inplace.vector.cons], construct/copy/destroy - constexpr inplace_vector() noexcept = default; - constexpr inplace_vector(const inplace_vector &) = default; - constexpr inplace_vector(inplace_vector &&) = default; - constexpr inplace_vector &operator=(const inplace_vector &) = default; - constexpr inplace_vector &operator=(inplace_vector &&) = default; + // [containers.sequences.inplace_vector.modifiers], modifiers - constexpr explicit inplace_vector(const size_type size) : base(size) { - base::uninitialized_value_construct(this->begin(), this->end()); + template + constexpr __T &unchecked_emplace_back(__Args &&...__args) + requires(constructible_from<__T, __Args...>) + { + __IV_EXPECT(size() < capacity() && "inplace_vector out-of-memory"); + construct_at(end(), ::std::forward<__Args>(__args)...); + __unsafe_set_size(size() + size_type(1)); + return back(); } - constexpr inplace_vector(const std::size_t size, const T &value) - : base(size) { - base::uninitialized_fill(this->begin(), this->end(), value); + template + constexpr __T *try_emplace_back(__Args &&...__args) { + if (size() == capacity()) [[unlikely]] + return nullptr; + return &unchecked_emplace_back(::std::forward<__Args>(__args)...); } -#ifdef __cpp_concepts - template - requires std::input_iterator - constexpr inplace_vector(InputIterator first, InputIterator last) : base() { - for (; first != last; ++first) { - emplace_back(*first); - } + template + constexpr void emplace_back(__Args &&...__args) + requires(constructible_from<__T, __Args...>) + { + if (!try_emplace_back(::std::forward<__Args>(__args)...)) [[unlikely]] + throw bad_alloc(); } - - template - requires std::forward_iterator - constexpr inplace_vector(InputIterator first, InputIterator last) - : base(std::distance(first, last)) { - if (first != last) { - base::uninitialized_copy(first, last, this->begin()); - } + constexpr __T &push_back(const __T &__x) + requires(constructible_from<__T, const __T &>) + { + emplace_back(__x); + return back(); } -#else - template constexpr inplace_vector(Itr first, Itr last) : base() { - for (; first != last; ++first) { - emplace_back(*first); - } + constexpr __T &push_back(__T &&__x) + requires(constructible_from<__T, __T &&>) + { + emplace_back(::std::forward<__T &&>(__x)); + return back(); } -#endif -#if defined(__cpp_lib_containers_ranges) && defined(__cpp_concepts) - template - requires container_compatible_range - constexpr inplace_vector(std::from_range_t, R &&rg) { - for (auto &&value : rg) { - emplace_back(std::forward(value)); - } + constexpr __T *try_push_back(const __T &__x) + requires(constructible_from<__T, const __T &>) + { + return try_emplace_back(__x); + } + constexpr __T *try_push_back(__T &&__x) + requires(constructible_from<__T, __T &&>) + { + return try_emplace_back(::std::forward<__T &&>(__x)); } -#endif - constexpr inplace_vector(std::initializer_list il) : base(il.size()) { - if (il.size() != 0) { - base::uninitialized_copy(il.begin(), il.end(), this->begin()); - } - }; // freestanding-deleted - constexpr inplace_vector &operator=(std::initializer_list il) { - if (Capacity < il.size()) { - throw std::bad_alloc(); - } - const auto diff = static_cast(il.size() - this->size()); - // The current size is greater - if (diff < 0) { - // if other.size is less than just copy normally - const iterator new_end = std::copy(il.begin(), il.end(), this->begin()); - // destroy the wasted memory - std::destroy(new_end, this->end()); - // The other size is greater than size - } else { - // copy other vector into the current vector until it runs ouf of size - std::copy(il.begin(), il.begin() + this->size(), this->begin()); - // copy the other half after the end of the current vector - base::uninitialized_copy(il.begin() + this->size(), il.end(), - this->end()); - } - this->change_size(diff); - return *this; - }; // freestanding-deleted - template - constexpr void assign(InputIterator first, InputIterator last) { - if (Capacity < std::distance(first, last)) { - throw std::bad_alloc(); - } - iterator end = this->end(); - for (iterator current = this->begin(); current != end; - ++current, void(++first)) { - if (first == last) { - std::destroy(current, end); - this->change_size(current - end); - return; - } - *current = size(); - } - for (; first != last; ++first) { - emplace_back(*first); - } - } // freestanding-deleted - -#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) - template - requires container_compatible_range - constexpr void assign_range(R &&rg) { - auto first = rg.begin(); - const auto last = rg.end(); - if (Capacity < std::ranges::distance(first, last)) { - throw std::bad_alloc(); - } - iterator end = this->end(); - for (iterator current = this->begin(); current != end; - ++current, (void)++first) { - if (first == last) { - std::destroy(current, end); - this->change_size(current - end); - return; - } - *current = *first; - } - for (; first != last; ++first) { - emplace_back(*first); - } - } // freestanding-deleted -#endif + constexpr __T &unchecked_push_back(const __T &__x) + requires(constructible_from<__T, const __T &>) + { + return unchecked_emplace_back(__x); + } + constexpr __T &unchecked_push_back(__T &&__x) + requires(constructible_from<__T, __T &&>) + { + return unchecked_emplace_back(::std::forward<__T &&>(__x)); + } - constexpr void assign(size_type n, const T &u) { - if (Capacity == 0) { - assert(size() == 0 && - "Cannot assign to inplace_vector with zero capacity"); - return; - } - const auto diff = static_cast(n - this->size()); - if (diff < 0) { - const pointer begin = this->begin(); - std::fill(begin, begin + size(), u); - std::destroy(begin + n, this->end()); - } else { - const iterator end = this->end(); - std::fill(this->begin(), end, u); - base::uninitialized_fill(end, end + diff, u); - } - this->change_size(diff); - }; // freestanding-deleted - constexpr void assign(std::initializer_list il) { - if (Capacity < il.size()) { - throw std::bad_alloc(); + template <__iv_detail::__container_compatible_range<__T> __R> + constexpr void append_range(__R &&__rg) + requires(constructible_from<__T, ranges::range_reference_t<__R>>) + { + if constexpr (ranges::sized_range<__R>) { + if (size() + ranges::size(__rg) > capacity()) [[unlikely]] + throw bad_alloc(); } - const auto diff = static_cast(il.size() - this->size()); - // other size is less than size - if (diff < 0) { - // if other.size is less than just copy normally - const iterator new_end = std::copy(il.begin(), il.end(), this->begin()); - std::destroy(new_end, this->end); - // other.size is greater than size - } else { - // copy other vector into the current vector until it runs ouf of size - std::copy(il.begin(), il.begin() + this->size(), this->begin()); - // copy the other half after the end of the current vector - base::uninitialized_copy(il.begin() + this->size(), il.end(), - this->end()); + for (auto &&__e : __rg) { + if (size() == capacity()) [[unlikely]] + throw bad_alloc(); + emplace_back(::std::forward(__e)); } - this->change_size(diff); - }; // freestanding-deleted + } - // [containers.sequences.inplace.vector.access], element access - constexpr reference at(size_type count) { - if (this->size() < count) { - throw std::out_of_range("inplace_vector::at"); - } - return *(this->begin() + count); + template + constexpr iterator emplace(const_iterator __position, __Args &&...__args) + requires(constructible_from<__T, __Args...> && movable<__T>) + { + __assert_iterator_in_range(__position); + auto __b = end(); + emplace_back(std::forward<__Args>(__args)...); + auto __pos = begin() + (__position - begin()); + rotate(__pos, __b, end()); + return __pos; } - constexpr const_reference at(const size_type count) const { - if (this->size() < count) { - throw std::out_of_range("inplace_vector::at"); + + template + constexpr iterator insert(const_iterator __position, __InputIterator __first, + __InputIterator __last) + requires(constructible_from<__T, iter_reference_t<__InputIterator>> && + movable<__T>) + { + __assert_iterator_in_range(__position); + __assert_valid_iterator_pair(__first, __last); + if constexpr (random_access_iterator<__InputIterator>) { + if (size() + static_cast(distance(__first, __last)) > + capacity()) [[unlikely]] + throw bad_alloc{}; } - return *(this->begin() + count); + auto __b = end(); + for (; __first != __last; ++__first) + emplace_back(::std::move(*__first)); + auto __pos = begin() + (__position - begin()); + rotate(__pos, __b, end()); + return __pos; } - constexpr reference operator[](const size_type count) noexcept { - return *(this->begin() + count); + template <__iv_detail::__container_compatible_range<__T> __R> + constexpr iterator insert_range(const_iterator __position, __R &&__rg) + requires(constructible_from<__T, ranges::range_reference_t<__R>> && + movable<__T>) + { + return insert(__position, ::std::begin(__rg), ::std::end(__rg)); } - constexpr const_reference operator[](const size_type count) const noexcept { - return *(this->begin() + count); + constexpr iterator insert(const_iterator __position, + initializer_list<__T> __il) + requires(constructible_from< + __T, ranges::range_reference_t>> && + movable<__T>) + { + return insert_range(__position, __il); } - constexpr reference front() noexcept { return *this->begin(); } - constexpr const_reference front() const noexcept { return *this->begin(); } - constexpr reference back() noexcept { return *(this->end() - 1); } - constexpr const_reference back() const noexcept { return *(this->end() - 1); } - - // inplace_vector.iterators - using base::begin; - using base::end; - - constexpr reverse_iterator rbegin() noexcept { - return reverse_iterator{this->end()}; + constexpr iterator insert(const_iterator __position, size_type __n, + const __T &__x) + requires(constructible_from<__T, const __T &> && copyable<__T>) + { + __assert_iterator_in_range(__position); + auto __b = end(); + for (size_type __i = 0; __i < __n; ++__i) + emplace_back(__x); + auto __pos = begin() + (__position - begin()); + rotate(__pos, __b, end()); + return __pos; } - constexpr const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator{this->end()}; + + constexpr iterator insert(const_iterator __position, const __T &__x) + requires(constructible_from<__T, const __T &> && copyable<__T>) + { + return insert(__position, 1, __x); } - constexpr reverse_iterator rend() noexcept { - return reverse_iterator{this->begin()}; + + constexpr iterator insert(const_iterator __position, __T &&__x) + requires(constructible_from<__T, __T &&> && movable<__T>) + { + return emplace(__position, ::std::move(__x)); } - constexpr const_reverse_iterator rend() const noexcept { - return const_reverse_iterator{this->begin()}; + + constexpr inplace_vector(initializer_list<__T> __il) + requires(constructible_from< + __T, ranges::range_reference_t>> && + movable<__T>) + { + insert(begin(), __il); } - constexpr const_iterator cbegin() const noexcept { return this->begin(); } - constexpr const_iterator cend() const noexcept { return this->end(); } - constexpr const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator{this->end()}; + + constexpr inplace_vector(size_type __n, const __T &__value) + requires(constructible_from<__T, const __T &> && copyable<__T>) + { + insert(begin(), __n, __value); } - constexpr const_reverse_iterator crend() const noexcept { - return const_reverse_iterator{this->begin()}; - } - - // [containers.sequences.inplace.vector.members] size/capacity - constexpr bool empty() const noexcept { return size() == 0; } - constexpr size_type size() const noexcept { return this->size_; } - static constexpr size_type max_size() noexcept { return Capacity; } - static constexpr size_type capacity() noexcept { return Capacity; } - constexpr void resize(size_type sz) { - const auto diff = static_cast(sz - this->size()); - if (diff < 0) { - std::destroy(this->begin() + sz, this->end()); - } else { - if (Capacity < size()) { - throw std::bad_alloc(); - } - const iterator end = this->end(); - base::uninitialized_value_construct(end, end + diff); - } - this->change_size(diff); - } // freestanding-deleted - constexpr void resize(size_type sz, const T &c) { - const auto diff = static_cast(sz - this->size()); - if (diff < 0) { - std::destroy(this->begin() + sz, this->end()); - } else { - if (Capacity < sz) { - throw std::bad_alloc(); - } - const iterator end = this->end(); - std::uninitialized_fill(end, end + diff, c); - } - this->change_size(diff); - } // freestanding-deleted - static constexpr void reserve(size_type sz) { - if (Capacity < sz) { - throw std::bad_alloc(); - } - } // freestanding-deleted - static constexpr void shrink_to_fit() noexcept {} + constexpr explicit inplace_vector(size_type __n) + requires(constructible_from<__T, __T &&> && default_initializable<__T>) + { + for (size_type __i = 0; __i < __n; ++__i) + emplace_back(__T{}); + } - // [containers.sequences.inplace.vector.modifiers], modifiers - template constexpr reference emplace_back(Args &&...args) { - if (this->size() == Capacity) { - throw std::bad_alloc(); - } - return this->unchecked_emplace_back(std::forward(args)...); - } // freestanding-deleted + template // BUGBUG: why not ranges::input_iterator? + constexpr inplace_vector(__InputIterator __first, __InputIterator __last) + requires(constructible_from<__T, iter_reference_t<__InputIterator>> && + movable<__T>) + { + insert(begin(), __first, __last); + } - constexpr reference push_back(const T &x) { - if (this->size() == Capacity) { - throw std::bad_alloc(); - } - return this->unchecked_emplace_back(x); - } // freestanding-deleted - constexpr reference push_back(T &&x) { - if (this->size() == Capacity) { - throw std::bad_alloc(); - } - return this->unchecked_emplace_back(std::move(x)); - } // freestanding-deleted - -#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) - template - requires container_compatible_range - constexpr void append_range(R &&rg) { - auto first = rg.begin(); - auto last = rg.end(); - for (; first != last; ++first) { - emplace_back(*first); - } - } // freestanding-deleted -#endif + template <__iv_detail::__container_compatible_range<__T> __R> + constexpr inplace_vector(from_range_t, __R &&__rg) + requires(constructible_from<__T, ranges::range_reference_t<__R>> && + movable<__T>) + { + insert_range(begin(), std::forward<__R &&>(__rg)); + } - constexpr void pop_back() { - if (!empty()) { - const auto end = this->end(); - std::destroy(end, end + 1); - this->change_size(-1); + constexpr iterator erase(const_iterator __first, const_iterator __last) + requires(movable<__T>) + { + __assert_iterator_pair_in_range(__first, __last); + iterator __f = begin() + (__first - begin()); + if (__first != __last) { + __unsafe_destroy(::std::move(__f + (__last - __first), end(), __f), + end()); + __unsafe_set_size(size() - static_cast(__last - __first)); } + return __f; } - template constexpr pointer try_emplace_back(Args &&...args) { - if (this->size() == Capacity) { - return nullptr; - } - return std::addressof( - this->unchecked_emplace_back(std::forward(args)...)); + constexpr iterator erase(const_iterator __position) + requires(movable<__T>) + { + return erase(__position, __position + 1); } - constexpr pointer - try_push_back(const T &x) noexcept(std::is_nothrow_copy_constructible_v) { - if (this->size() == Capacity) { - return nullptr; - } - return std::addressof(this->unchecked_emplace_back(x)); + + constexpr void clear() noexcept { + __unsafe_destroy(begin(), end()); + __unsafe_set_size(0); } - constexpr pointer - try_push_back(T &&x) noexcept(std::is_nothrow_move_constructible_v) { - if (this->size() == Capacity) { - return nullptr; + constexpr void resize(size_type __sz, const __T &__c) + requires(constructible_from<__T, const __T &> && copyable<__T>) + { + if (__sz == size()) + return; + else if (__sz > __N) [[unlikely]] + throw bad_alloc{}; + else if (__sz > size()) + insert(end(), __sz - size(), __c); + else { + __unsafe_destroy(begin() + __sz, end()); + __unsafe_set_size(__sz); } - return std::addressof(this->unchecked_emplace_back(std::move(x))); - } - - /* - template - constexpr try_append_range(R &&rg) noexcept( - std::is_nothrow_move_constructible_v) { - auto first = range.begin(); - auto last = range.end(); - for (; this->size() != Capacity && first != last; ++first) { - emplace_back(*first); - } - return first; + } + constexpr void resize(size_type __sz) + requires(constructible_from<__T, __T &&> && default_initializable<__T>) + { + if (__sz == size()) + return; + else if (__sz > __N) [[unlikely]] + throw bad_alloc{}; + else if (__sz > size()) + while (size() != __sz) + emplace_back(__T{}); + else { + __unsafe_destroy(begin() + __sz, end()); + __unsafe_set_size(__sz); } - */ - - template - constexpr reference unchecked_emplace_back(Args &&...args) { -#ifdef __cpp_constexpr_dynamic_alloc - auto final = std::construct_at(end(), std::forward(args)...); -#else - // Note: placement-new may not be constexpr friendly - // Avoiding placement-new may allow inplace_vector to be constexpr friendly - auto final = ::new (end()) T(std::forward(args)...); -#endif - this->change_size(1); - return *final; } - constexpr reference - unchecked_push_back(const T &x) noexcept(std::is_nothrow_constructible_v) { - return this->unchecked_emplace_back(x); + constexpr reference at(size_type __pos) { + if (__pos >= size()) [[unlikely]] + throw out_of_range("inplace_vector::at"); + return __iv_detail::__index(*this, __pos); } - constexpr reference unchecked_push_back(T &&x) { - return this->unchecked_emplace_back(std::move(x)); + constexpr const_reference at(size_type __pos) const { + if (__pos >= size()) [[unlikely]] + throw out_of_range("inplace_vector::at"); + return __iv_detail::__index(*this, __pos); } - template - constexpr iterator emplace(const_iterator position, Args &&...args) { - const iterator pos = position; - const iterator end = this->end(); - if (this->size() == Capacity) { - throw std::bad_alloc(); - } else if (pos < this->begin() || end < pos) { - throw std::out_of_range( - "inplace_vector::emplace(const_iterator, Args...)"); - } - if (pos == end) { - this->unchecked_emplace_back(std::forward(args)...); - } else { - T temp{std::forward(args)...}; - this->unchecked_emplace_back(std::move(*(end - 1))); - std::move_backward(pos, end - 1, end); - *pos = std::move(temp); - } - return pos; - } // freestanding-deleted - constexpr iterator insert(const_iterator position, const T &x) { - return emplace(position, x); - } // freestanding-deleted - constexpr iterator insert(const_iterator position, T &&x) { - return emplace(position, std::move(x)); - } // freestanding-deleted - constexpr iterator insert(const_iterator position, size_type n, const T &x) { - const iterator pos = position; - const iterator end = this->end(); - - if (this->size() + n < Capacity) { - throw std::bad_alloc(); - } else if (pos < this->begin() || end < pos()) { - throw std::out_of_range( - "inplace_vector::insert(const_iterator, size_type, T)"); - } - if (n == 0) { - return pos; - } - if (pos == end) { - base::uninitialized_fill(end, end + n, x); - this->change_size(n); - return pos; - } - const pointer middle = pos + n; - if (end <= middle) { - base::uninitialized_fill(end, middle, x); - base::uninitialized_move(pos, end, middle); - std::fill(pos, end, x); - } else { - base::uninitialized_move(end - n, end, end); - std::move_backward(pos, end - n, end); - std::fill(pos, middle, x); - } - this->change_size(n); - return pos; - } - - template - constexpr inplace_vector::iterator - insert(const_iterator position, InputIterator first, InputIterator last) { - const iterator pos = position; - const iterator end = this->end(); - const auto count = std::distance(first, last); - - if (this->size() + count > Capacity) { - throw std::bad_alloc(); - } else if (pos < this->begin() || end < pos) { - throw std::out_of_range( - "inplace_vector::insert(const_iterator, Iter, Iter)"); - } - if (count == 0) { - return pos; - } - if (pos == end) { - base::uninitialized_copy(first, last, end); - this->change_size(count); - return pos; - } - const pointer middle = pos + count; - const auto to_copy = end - pos; - if (end <= middle) { - InputIterator imiddle = std::next(first, to_copy); - base::uninitialized_copy(imiddle, last, end); - base::uninitialized_move(pos, end, middle); - std::copy(first, imiddle, pos); - } else { - base::uninitialized_move(end - count, end, end); - std::move_backward(pos, end - count, end); - std::copy(first, last, pos); - } - } // freestanding-deleted -#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) - template - requires container_compatible_range - constexpr iterator insert_range(const_iterator position, R &&rg) { - const iterator old_end = this->end(); - auto first = rg.begin(); - auto last = rg.end(); - - for (; first != last; ++first) { - emplace_back(*first); - } - const iterator pos = position; - std::rotate(pos, old_end, this->end()); - return pos; - } // freestanding-deleted -#endif - constexpr iterator insert(const_iterator position, - std::initializer_list il) { - const iterator pos = position; - const iterator end = this->end(); - auto count = il.size(); - if (this->size() + count < Capacity) { - throw std::bad_alloc(); - } else if (pos < this->begin() || end < pos) { - throw std::out_of_range( - "inplace_vector::insert(const_iterator, initializer_list)"); - } - if (count == 0) { - return pos; - } - if (pos == end) { - base::uninitialized_copy(il.begin(), il.end(), end); - this->cange_size(count); - return pos; - } - } // freestanding-deleted - constexpr iterator erase(const_iterator position) { - const iterator pos = position; - const iterator end = this->end(); + constexpr void pop_back() { + __IV_EXPECT(size() > 0 && "pop_back from empty inplace_vector!"); + __unsafe_destroy(end() - 1, end()); + __unsafe_set_size(size() - 1); + } - if (pos == end) { - return pos; - } - if (this->size() == 0 || pos < this->begin() || end < pos) { - std::terminate(); - } - std::move(pos + 1, end, pos); - this->change_size(-1); - std::destroy(end - 1, end); - return pos; - } - constexpr iterator erase(const_iterator cfirst, const_iterator clast) { - const iterator first = (iterator)cfirst; - const iterator last = (iterator)clast; - const iterator end = this->end(); - if (first == last) { - return last; - } - if (first < this->begin() || end < last) { - std::terminate(); - } - const auto new_end = std::move(last, end, first); - std::destroy(new_end, end); - this->change_size(first - last); - return first; + constexpr inplace_vector(const inplace_vector &__x) + requires(copyable<__T>) + { + for (auto &&__e : __x) + emplace_back(__e); } - constexpr void - swap(inplace_vector &x) noexcept(Capacity == 0 || - (std::is_nothrow_swappable_v && - std::is_nothrow_move_constructible_v)) { - if (this->size() < x.size()) { - const auto new_mid = - std::swap_ranges(this->begin(), this->end(), x.begin()); - base::uninitialized_move(new_mid, x.end(), this->end()); - std::destroy(new_mid, x.end()); - } else { - const auto new_mid = std::swap_ranges(x.begin(), x.end(), this->begin()); - base::uninitialized_move(new_mid, this->end(), x.end()); - std::destroy(new_mid, this->end()); - } - const auto diff = static_cast(this->size() - x.size()); - this->change_size(diff); - x.change_size(-diff); + constexpr inplace_vector(inplace_vector &&__x) + requires(movable<__T>) + { + for (auto &&__e : __x) + emplace_back(::std::move(__e)); } - constexpr void clear() noexcept { - std::destroy(this->begin(), this->end()); - this->change_size(static_cast(this->size())); + constexpr inplace_vector &operator=(const inplace_vector &__x) + requires(copyable<__T>) + { + clear(); + for (auto &&__e : __x) + emplace_back(__e); + return *this; + } + constexpr inplace_vector &operator=(inplace_vector &&__x) + requires(movable<__T>) + { + clear(); + for (auto &&__e : __x) + emplace_back(::std::move(__e)); + return *this; } - constexpr friend bool - operator==(const inplace_vector &x, const inplace_vector &y) noexcept( - noexcept(std::equal(x.begin(), x.end(), y.begin(), y.end()))) { - return std::equal(x.begin(), x.end(), y.begin(), y.end()); + constexpr void swap(inplace_vector &__x) noexcept( + __N == 0 || + (is_nothrow_swappable_v<__T> && is_nothrow_move_constructible_v<__T>)) + requires(movable<__T>) + { + auto tmp = ::std::move(__x); + __x = ::std::move(*this); + (*this) = ::std::move(tmp); } -#ifdef __cpp_lib_three_way_comparison - constexpr friend std::compare_three_way_result - operator<=>(const inplace_vector &x, const inplace_vector &y) { - return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + template + constexpr void assign(__InputIterator __first, __InputIterator __last) + requires(constructible_from<__T, iter_reference_t<__InputIterator>> && + movable<__T>) + { + clear(); + insert(begin(), __first, __last); } -#endif + template <__iv_detail::__container_compatible_range<__T> __R> + constexpr void assign_range(__R &&__rg) + requires(constructible_from<__T, ranges::range_reference_t<__R>> && + movable<__T>) + { + assign(begin(__rg), end(__rg)); + } + constexpr void assign(size_type __n, const __T &__u) + requires(constructible_from<__T, const __T &> && movable<__T>) + { + clear(); + insert(begin(), __n, __u); + } + constexpr void assign(initializer_list<__T> __il) + requires(constructible_from< + __T, ranges::range_reference_t>> && + movable<__T>) + { + clear(); + insert_range(begin(), __il); + } + + constexpr friend int /*synth-three-way-result*/ + operator<=>(const inplace_vector & __x, const inplace_vector & __y) { + if (__x.size() < __y.size()) + return -1; + if (__x.size() > __y.size()) + return +1; + + bool __all_equal = true; + bool __all_less = true; + for (size_type __i = 0; __i < __x.size(); ++__i) { + if (__x[__i] < __y[__i]) + __all_equal = false; + if (__x[__i] == __y[__i]) + __all_less = false; + } - constexpr friend void swap(inplace_vector &x, inplace_vector &y) noexcept( - Capacity == 0 || (std::is_nothrow_swappable_v && - std::is_nothrow_move_constructible_v)) { - x.swap(y); + if (__all_equal) + return 0; + if (__all_less) + return -1; + return 1; } }; -} // namespace beman::inplace_vector + +} // namespace beman + +// undefine all the internal macros +#undef __IV_ASSUME +#undef __IV_ASSERT +#undef __IV_EXPECT diff --git a/tests/beman/inplace_vector/CMakeLists.txt b/tests/beman/inplace_vector/CMakeLists.txt index 0dc7351..9b10181 100644 --- a/tests/beman/inplace_vector/CMakeLists.txt +++ b/tests/beman/inplace_vector/CMakeLists.txt @@ -9,3 +9,14 @@ add_executable(beman.inplace_vector.test inplace_vector.test.cpp) target_link_libraries(beman.inplace_vector.test PRIVATE beman.inplace_vector) add_test(NAME beman.inplace_vector.test COMMAND beman.inplace_vector.test) + +# Migrated test from original implementation +add_executable(beman.inplace_vector.ref-test ref_impl.test.cpp) +target_link_libraries( + beman.inplace_vector.ref-test + PRIVATE beman.inplace_vector +) +add_test( + NAME beman.inplace_vector.ref-test + COMMAND beman.inplace_vector.ref-test +) diff --git a/tests/beman/inplace_vector/inplace_vector.test.cpp b/tests/beman/inplace_vector/inplace_vector.test.cpp index d4b7372..fee9c6b 100644 --- a/tests/beman/inplace_vector/inplace_vector.test.cpp +++ b/tests/beman/inplace_vector/inplace_vector.test.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include -using namespace beman::inplace_vector; +using namespace beman; template constexpr void test() { using vec = inplace_vector; diff --git a/tests/beman/inplace_vector/ref_impl.test.cpp b/tests/beman/inplace_vector/ref_impl.test.cpp new file mode 100644 index 0000000..3e7cec7 --- /dev/null +++ b/tests/beman/inplace_vector/ref_impl.test.cpp @@ -0,0 +1,1149 @@ +#pragma GCC diagnostic ignored "-Wsign-compare" + +/// \file +/// +/// Test for inline_vector +/// +/// Most of the tests below are adapted from libc++: https://libcxx.llvm.org +/// under the following license: +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include +#include +#include + +#define CHECK(...) \ + static_cast((__VA_ARGS__) \ + ? void(0) \ + : ::beman::__iv_detail::__assert_failure( \ + static_cast(__FILE__), __LINE__, \ + "assertion failed: " #__VA_ARGS__)) + +#define CHECK_THROWS(EXPR, EXCEPT) \ + if (auto e = \ + [&] { \ + try { \ + (EXPR); \ + return false; \ + } catch (const EXCEPT &) { \ + return true; \ + } catch (...) { \ + return false; \ + } \ + }(); \ + !e) { \ + ::beman::__iv_detail::__assert_failure( \ + static_cast(__FILE__), __LINE__, \ + "expression failed to throw " #EXCEPT ": " #EXPR); \ + } + +template struct beman::__iv_detail::__storage::__zero_sized; +template struct beman::__iv_detail::__storage::__trivial; +template struct beman::__iv_detail::__storage::__non_trivial< + std::unique_ptr, 10>; + +template struct beman::__iv_detail::__storage::__zero_sized; +template struct beman::__iv_detail::__storage::__trivial; +template struct beman::__iv_detail::__storage::__non_trivial< + const std::unique_ptr, 10>; + +// empty: +template struct beman::inplace_vector; + +// trivial non-empty: +template struct beman::inplace_vector; +template struct beman::inplace_vector; +template struct beman::inplace_vector; + +// non-trivial +template struct beman::inplace_vector; +template struct beman::inplace_vector; + +// move-only: +template struct beman::inplace_vector, 3>; +template struct beman::inplace_vector, 3>; + +struct tint { + std::size_t i; + tint() = default; + constexpr tint(tint const &) = default; + constexpr tint(tint &&) = default; + constexpr tint &operator=(tint const &) = default; + constexpr tint &operator=(tint &&) = default; + // FIXME: ~tint() = default; + // ^^^ adding this makes the class non-trivial in clang + + explicit constexpr tint(std::size_t j) : i(j) {} + explicit operator std::size_t() { return i; } +}; + +static_assert(std::is_trivial{} && std::is_copy_constructible{} && + std::is_move_constructible{}, + ""); + +// Explicit instantiations +template struct beman::inplace_vector; // trivial empty +template struct beman::inplace_vector; // trivial non-empty +template struct beman::inplace_vector; // trivial non-empty +template struct beman::inplace_vector; // trivial non-empty + +struct moint final { + std::size_t i = 0; + moint() = default; + moint(moint const &) = delete; + moint &operator=(moint const &) = delete; + moint(moint &&) = default; + moint &operator=(moint &&) = default; + ~moint() = default; + explicit operator std::size_t() { return i; } + explicit constexpr moint(std::size_t j) : i(j) {} + // it seems that deleting the copy constructor is not enough to make + // this non-trivial using libstdc++: + virtual void foo() {} + bool operator==(moint b) { return i == b.i; } +}; + +static_assert(!std::is_trivial{} and + !std::is_copy_constructible{} and + std::is_move_constructible{}, + ""); + +// cannot explicitly instantiate the type for some types +// // non-trivial empty: +// template struct std::inplace_vector; +// // non-trivial non-empty: +// template struct std::inplace_vector; +// template struct std::inplace_vector; +// template struct std::inplace_vector; + +template using vector = beman::inplace_vector; + +class non_copyable { + int i_; + double d_; + +public: + non_copyable(const non_copyable &) = delete; + non_copyable &operator=(const non_copyable &) = delete; + + non_copyable(int i, double d) : i_(i), d_(d) {} + + non_copyable(non_copyable &&a) noexcept : i_(a.i_), d_(a.d_) { + a.i_ = 0; + a.d_ = 0; + } + + non_copyable &operator=(non_copyable &&a) noexcept { + i_ = a.i_; + d_ = a.d_; + a.i_ = 0; + a.d_ = 0; + return *this; + } + + int geti() const { return i_; } + double getd() const { return d_; } +}; + +template struct vec { + vec() = default; + vec(std::initializer_list /*il*/) {} +}; + +template constexpr void test_il_constructor() { + auto v = [] { + switch (N) { + case 0: { + const vector x{}; + return x; + } + case 1: { + const vector x({0}); + return x; + } + case 10: { + const vector x({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + return x; + } + default: + CHECK(false); + } + }(); + CHECK(v.size() == N); + for (size_t i = 0; i < N; ++i) + CHECK(v[i] == T(i)); + CHECK(11 > N); + if !consteval { + CHECK_THROWS( + ([&] { const vector x({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); }()), + std::bad_alloc); + } +} + +template constexpr void test_il_assignment() { + auto v = [] { + switch (N) { + case 0: { + const vector x = {}; + return x; + } + case 1: { + const vector x = {0}; + return x; + } + case 10: { + const vector x = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + return x; + } + default: + CHECK(false); + } + }(); + CHECK(v.size() == N); + for (size_t i = 0; i < N; ++i) + CHECK(v[i] == T(i)); + if !consteval { + CHECK_THROWS(([&] { + [[maybe_unused]] const vector x = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10}; + }()), + std::bad_alloc); + } +} + +template constexpr void test_default_constructor() { + vector v; + CHECK(v.size() == 0); + CHECK(v.empty()); + CHECK(v.capacity() == N); +} + +template +constexpr void +test_default_constructor_bounds_and_contiguous_iterators(std::size_t sz) { + CHECK(sz <= N); + vector v(sz); + CHECK(v.size() == sz); + CHECK(v.max_size() == N); + CHECK(v.capacity() == N); + for (std::size_t i = 0; i != sz; ++i) { + CHECK(v[i] == T{}); + } + for (std::size_t i = 0; i < v.size(); ++i) { // contiguous + CHECK(*(v.begin() + i) == *(std::addressof(*v.begin()) + i)); + CHECK(*(v.cbegin() + i) == *(std::addressof(*v.cbegin()) + i)); + CHECK(*(v.rbegin() + i) == *(std::addressof(*v.rbegin()) - i)); + CHECK(*(v.crbegin() + i) == *(std::addressof(*v.crbegin()) - i)); + } + // iterators + if (v.size() == 0) { + CHECK(v.empty()); + CHECK(v.begin() == v.end()); + CHECK(v.cbegin() == v.cend()); + CHECK(v.rbegin() == v.rend()); + CHECK(v.crbegin() == v.crend()); + } else { + CHECK(!v.empty()); + CHECK(v.begin() != v.end()); + CHECK(v.cbegin() != v.cend()); + CHECK(v.rbegin() != v.rend()); + CHECK(v.crbegin() != v.crend()); + CHECK(std::distance(v.begin(), v.end()) == v.size()); + CHECK(std::distance(v.cbegin(), v.cend()) == v.size()); + CHECK(std::distance(v.rbegin(), v.rend()) == v.size()); + CHECK(std::distance(v.crbegin(), v.crend()) == v.size()); + CHECK(v.back() == T{}); + CHECK(v.front() == T{}); + } +} + +template constexpr void test_iterators() { + { // N3644 testing + using C = vector; + typename C::iterator ii1{}, ii2{}; + typename C::iterator ii4 = ii1; + typename C::const_iterator cii{}; + CHECK(ii1 == ii2); + CHECK(ii1 == ii4); + + CHECK(!(ii1 != ii2)); + + CHECK((ii1 == cii)); + CHECK((cii == ii1)); + CHECK(!(ii1 != cii)); + CHECK(!(cii != ii1)); + CHECK(!(ii1 < cii)); + CHECK(!(cii < ii1)); + CHECK((ii1 <= cii)); + CHECK((cii <= ii1)); + CHECK(!(ii1 > cii)); + CHECK(!(cii > ii1)); + CHECK((ii1 >= cii)); + CHECK((cii >= ii1)); + CHECK((cii - ii1) == 0); + CHECK((ii1 - cii) == 0); + } +} + +template +constexpr void test_constructor_input_iterators() { + CHECK(N < 11); + const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + using C = vector; + C c(std::begin(t), std::begin(t) + N); + CHECK(c.size() == N); + CHECK( + std::equal(std::begin(t), std::begin(t) + N, std::begin(c), std::end(c))); + + for (typename C::iterator i = c.begin(); i != c.end(); ++i) { + auto idx = i - c.begin(); + auto o = t[idx]; + CHECK(*i == o); + *i = 2 * o; + CHECK(*i == (2 * o)); + CHECK(std::distance(c.begin(), i) == idx); + CHECK(std::distance(i, c.end()) == c.size() - idx); + } +} + +template constexpr bool test_all_() { + test_il_constructor(); + test_il_assignment(); + test_default_constructor(); + for (size_t i = 0; i < N; ++i) + test_default_constructor_bounds_and_contiguous_iterators(i); + test_iterators(); + test_constructor_input_iterators(); + return true; +} + +template void test_all() { + constexpr bool ct = test_all_(); + static_assert(ct, "CONSTEXPR TESTS FAILED"); + test_all_(); +} + +int main() { + { // storage + using beman::__iv_detail::__storage::__non_trivial; + using beman::__iv_detail::__storage::__trivial; + using beman::__iv_detail::__storage::__zero_sized; + using beman::__iv_detail::__storage::_t; + + static_assert(std::is_same<_t, __zero_sized>{}); + static_assert(std::is_same<_t, __trivial>{}); + static_assert(std::is_same<_t, 10>, + __non_trivial, 10>>{}, + ""); + } + + test_all(); + test_all(); + test_all(); + + // test_all(); + + { // capacity + vector a; + static_assert(a.capacity() == std::size_t(10)); + CHECK(a.empty()); + for (std::size_t i = 0; i != 10; ++i) { + a.push_back(0); + } + static_assert(a.capacity() == std::size_t(10)); + CHECK(a.size() == std::size_t(10)); + CHECK(!a.empty()); + CHECK_THROWS(a.push_back(0), std::bad_alloc); + CHECK((uintptr_t) nullptr == (uintptr_t)a.try_push_back(0)); + } + + { // resize copyable + using Copyable = int; + vector a(std::size_t(10), 5); + CHECK(a.size() == std::size_t(10)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + for (std::size_t i = 0; i != 10; ++i) { + CHECK(a[i] == 5); + } + a.resize(5); + CHECK(a.size() == std::size_t(5)); + + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + a.resize(9); + CHECK(a[4] == 5); + for (std::size_t i = 5; i != 9; ++i) { + CHECK(a[i] == 0); + } + CHECK(a.size() == std::size_t(9)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + a.resize(10, 3); + CHECK(a[4] == 5); + CHECK(a[8] == 0); + CHECK(a[9] == 3); + CHECK(a.size() == std::size_t(10)); + static_assert(a.capacity() == std::size_t(10)); + a.resize(5, 2); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(a[i] == 5); + } + // test_contiguous(a); + CHECK_THROWS(a.resize(12), std::bad_alloc); + } + { // resize move-only + using MoveOnly = std::unique_ptr; + vector a(10); + CHECK(a.size() == std::size_t(10)); + static_assert(a.capacity() == std::size_t(10)); + a.resize(5); + // test_contiguous(a); + CHECK(a.size() == std::size_t(5)); + static_assert(a.capacity() == std::size_t(10)); + a.resize(9); + CHECK(a.size() == std::size_t(9)); + static_assert(a.capacity() == std::size_t(10)); + } + + { // resize value: + using Copyable = int; + vector a(std::size_t(10)); + CHECK(a.size() == std::size_t(10)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + for (std::size_t i = 0; i != 10; ++i) { + CHECK(a[i] == 0); + } + a.resize(5); + CHECK(a.size() == std::size_t(5)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(a[i] == 0); + } + a.resize(9, 5); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(a[i] == 0); + } + for (std::size_t i = 5; i != 9; ++i) { + CHECK(a[i] == 5); + } + CHECK(a.size() == std::size_t(9)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + a.resize(10, 3); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(a[i] == 0); + } + for (std::size_t i = 5; i != 9; ++i) { + CHECK(a[i] == 5); + } + CHECK(a[9] == 3); + CHECK(a.size() == std::size_t(10)); + static_assert(a.capacity() == std::size_t(10)); + // test_contiguous(a); + } + + { // assign copy + vector z(3, 5); + vector a = {0, 1, 2}; + CHECK(a.size() == std::size_t{3}); + vector b; + CHECK(b.size() == std::size_t{0}); + b = a; + CHECK(b.size() == std::size_t{3}); + CHECK(std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b))); + } + + { // copy construct + vector a = {0, 1, 2}; + CHECK(a.size() == std::size_t{3}); + vector b(a); + CHECK(b.size() == std::size_t{3}); + + CHECK(std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b))); + } + + { // assign move + using MoveOnly = std::unique_ptr; + vector a(3); + CHECK(a.size() == std::size_t{3}); + vector b; + CHECK(b.size() == std::size_t{0}); + b = std::move(a); + CHECK(b.size() == std::size_t{3}); + CHECK(a.size() == std::size_t{3}); + } + + { // move construct + using MoveOnly = std::unique_ptr; + vector a(3); + CHECK(a.size() == std::size_t{3}); + vector b(std::move(a)); + CHECK(b.size() == std::size_t{3}); + CHECK(a.size() == std::size_t{3}); + } + + { // old tests + using vec_t = vector; + vec_t vec1(5); + vec1[0] = 0; + vec1[1] = 1; + vec1[2] = 2; + vec1[3] = 3; + vec1[4] = 4; + { + vec_t vec2; + vec2.push_back(5); + vec2.push_back(6); + vec2.push_back(7); + vec2.push_back(8); + vec2.push_back(9); + CHECK(vec1[0] == 0); + CHECK(vec1[4] == 4); + CHECK(vec2[0] == 5); + CHECK(vec2[4] == 9); + } + { + vec_t vec2; + vec2.try_push_back(5); + vec2.try_push_back(6); + vec2.try_push_back(7); + vec2.try_push_back(8); + vec2.try_push_back(9); + CHECK(vec1[0] == 0); + CHECK(vec1[4] == 4); + CHECK(vec2[0] == 5); + CHECK(vec2[4] == 9); + } + { + vec_t vec2; + vec2.unchecked_push_back(5); + vec2.unchecked_push_back(6); + vec2.unchecked_push_back(7); + vec2.unchecked_push_back(8); + vec2.unchecked_push_back(9); + CHECK(vec1[0] == 0); + CHECK(vec1[4] == 4); + CHECK(vec2[0] == 5); + CHECK(vec2[4] == 9); + } + { + auto vec2 = vec1; + CHECK(vec2[0] == 0); + CHECK(vec2[4] == 4); + CHECK(vec1[0] == 0); + CHECK(vec1[4] == 4); + } + { + int count_ = 0; + for (auto i : vec1) { + CHECK(i == count_); + count_++; + } + } + + { + std::vector vec2(5); + vec2[0] = 4; + vec2[1] = 3; + vec2[2] = 2; + vec2[3] = 1; + vec2[4] = 0; + vec_t vec(vec2.size()); + copy(std::begin(vec2), std::end(vec2), std::begin(vec)); + int count_ = 4; + for (auto i : vec) { + CHECK(i == count_); + count_--; + } + } + } + { + using vec_t = vector; + static_assert(sizeof(vec_t) == 1, ""); + + constexpr auto a = vec_t{}; + static_assert(a.size() == std::size_t{0}, ""); + } + + { // back and front: + using C = vector; + C c(1); + CHECK(c.back() == 0); + CHECK(c.front() == 0); + CHECK(c[0] == 0); + c.clear(); + int one = 1; + c.push_back(one); + CHECK(c.back() == 1); + CHECK(c.front() == 1); + CHECK(c[0] == 1); + CHECK(c.size() == 1); + c.push_back(2); + CHECK(c.back() == 2); + CHECK(c.front() == 1); + CHECK(c[0] == 1); + CHECK(c[1] == 2); + CHECK(c.size() == 2); + c.pop_back(); + CHECK(c.front() == 1); + CHECK(c[0] == 1); + CHECK(c.back() == 1); + c.pop_back(); + CHECK(c.empty()); + } + + { // const back: + using C = vector; + constexpr C c(1); + static_assert(c.back() == 0); + static_assert(c.front() == 0); + static_assert(c[0] == 0); + static_assert(c.size() == 1); + } + + { // swap: same type + using C = vector; + C c0(3, 5); + C c1(5, 1); + C c2(0); + CHECK(c0.size() == std::size_t(3)); + CHECK(c1.size() == std::size_t(5)); + CHECK(c2.size() == std::size_t(0)); + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c0[i] == 5); + } + for (std::size_t i = 0; i != 5; ++i) { + CHECK(c1[i] == 1); + } + c0.swap(c1); + CHECK(c0.size() == std::size_t(5)); + CHECK(c1.size() == std::size_t(3)); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(c0[i] == 1); + } + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c1[i] == 5); + } + c2.swap(c1); + CHECK(c1.size() == std::size_t(0)); + CHECK(c2.size() == std::size_t(3)); + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c2[i] == 5); + } + } + + { // std::swap: same type + using C = vector; + C c0(3, 5); + C c1(5, 1); + C c2(0); + CHECK(c0.size() == std::size_t(3)); + CHECK(c1.size() == std::size_t(5)); + CHECK(c2.size() == std::size_t(0)); + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c0[i] == 5); + } + for (std::size_t i = 0; i != 5; ++i) { + CHECK(c1[i] == 1); + } + std::swap(c0, c1); + CHECK(c0.size() == std::size_t(5)); + CHECK(c1.size() == std::size_t(3)); + for (std::size_t i = 0; i != 5; ++i) { + CHECK(c0[i] == 1); + } + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c1[i] == 5); + } + std::swap(c2, c1); + CHECK(c1.size() == std::size_t(0)); + CHECK(c2.size() == std::size_t(3)); + for (std::size_t i = 0; i != 3; ++i) { + CHECK(c2[i] == 5); + } + } + + { + constexpr vector v; + static_assert(v.data() != nullptr); + + constexpr vector v0; + static_assert(v0.data() == nullptr); + } + + { // emplace: + { + vector c; + vector::iterator i = c.emplace(c.cbegin(), 2, 3.5); + CHECK(i == c.begin()); + CHECK(c.size() == 1); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + i = c.emplace(c.cend(), 3, 4.5); + CHECK(i == c.end() - 1); + CHECK(c.size() == 2); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + i = c.emplace(c.cbegin() + 1, 4, 6.5); + CHECK(i == c.begin() + 1); + CHECK(c.size() == 3); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c[1].geti() == 4); + CHECK(c[1].getd() == 6.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + CHECK_THROWS(c.emplace(c.cbegin(), 2, 3.5), std::bad_alloc); + } + { + vector c; + vector::iterator i = c.emplace(c.cbegin(), 2, 3.5); + CHECK(i == c.begin()); + CHECK(c.size() == 1); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + i = c.emplace(c.cend(), 3, 4.5); + CHECK(i == c.end() - 1); + CHECK(c.size() == 2); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + i = c.emplace(c.cbegin() + 1, 4, 6.5); + CHECK(i == c.begin() + 1); + CHECK(c.size() == 3); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c[1].geti() == 4); + CHECK(c[1].getd() == 6.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + } + } + + { // emplace_back + vector c; + c.emplace_back(2, 3.5); + CHECK(c.size() == 1); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + c.emplace_back(3, 4.5); + CHECK(c.size() == 2); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + CHECK_THROWS(c.emplace_back(2, 3.5), std::bad_alloc); + } + { // try_emplace_back + vector c; + CHECK((uintptr_t)c.begin() == (uintptr_t)c.try_emplace_back(2, 3.5)); + CHECK(c.size() == 1); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK((uintptr_t)(c.begin() + 1) == (uintptr_t)c.try_emplace_back(3, 4.5)); + CHECK(c.size() == 2); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + CHECK((uintptr_t) nullptr == (uintptr_t)c.try_emplace_back(2, 3.5)); + } + { // unchecked_emplace_back + vector c; + CHECK((uintptr_t)c.begin() == (uintptr_t)&c.unchecked_emplace_back(2, 3.5)); + CHECK(c.size() == 1); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK((uintptr_t)(c.begin() + 1) == + (uintptr_t)&c.unchecked_emplace_back(3, 4.5)); + CHECK(c.size() == 2); + CHECK(c.front().geti() == 2); + CHECK(c.front().getd() == 3.5); + CHECK(c.back().geti() == 3); + CHECK(c.back().getd() == 4.5); + } + + { // emplace extra: + { // + vector v; + v = {1, 2, 3}; + + v.emplace(v.begin(), v.back()); + CHECK(v[0] == 3); + } + { + vector v; + v = {1, 2, 3}; + v.emplace(v.begin(), v.back()); + CHECK(v[0] == 3); + } + } + + { // erase + { + int a1[] = {1, 2, 3}; + vector l1(a1, a1 + 3); + CHECK(l1.size() == 3); + vector::const_iterator i = l1.begin(); + ++i; + vector::iterator j = l1.erase(i); + CHECK(l1.size() == 2); + CHECK(std::distance(l1.begin(), l1.end()) == 2); + CHECK(*j == 3); + CHECK(*l1.begin() == 1); + CHECK(*std::next(l1.begin()) == 3); + j = l1.erase(j); + CHECK(j == l1.end()); + CHECK(l1.size() == 1); + CHECK(std::distance(l1.begin(), l1.end()) == 1); + CHECK(*l1.begin() == 1); + j = l1.erase(l1.begin()); + CHECK(j == l1.end()); + CHECK(l1.empty()); + CHECK(std::distance(l1.begin(), l1.end()) == 0); + } + } + + { // erase iter iter + int a1[] = {1, 2, 3}; + using vec_t = vector; + { + vec_t l1(a1, a1 + 3); + vec_t::iterator i = l1.erase(l1.cbegin(), l1.cbegin()); + CHECK(l1.size() == 3); + CHECK(std::distance(l1.cbegin(), l1.cend()) == 3); + CHECK(i == l1.begin()); + } + { + vec_t l1(a1, a1 + 3); + vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin())); + CHECK(l1.size() == 2); + CHECK(std::distance(l1.cbegin(), l1.cend()) == 2); + CHECK(i == l1.begin()); + CHECK(l1 == vec_t(a1 + 1, a1 + 3)); + } + { + vec_t l1(a1, a1 + 3); + vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin(), 2)); + CHECK(l1.size() == 1); + CHECK(std::distance(l1.cbegin(), l1.cend()) == 1); + CHECK(i == l1.begin()); + CHECK(l1 == vec_t(a1 + 2, a1 + 3)); + } + { + vec_t l1(a1, a1 + 3); + vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin(), 3)); + CHECK(l1.empty()); + CHECK(std::distance(l1.cbegin(), l1.cend()) == 0); + CHECK(i == l1.begin()); + } + { + vector outer(2, vec_t(1)); + outer.erase(outer.begin(), outer.begin()); + CHECK(outer.size() == 2); + CHECK(outer[0].size() == 1); + CHECK(outer[1].size() == 1); + } + } + + { // insert init list + { + vector d(10, 1); + vector::iterator i = d.insert(d.cbegin() + 2, {3, 4, 5, 6}); + CHECK(d.size() == 14); + CHECK(i == d.begin() + 2); + CHECK(d[0] == 1); + CHECK(d[1] == 1); + CHECK(d[2] == 3); + CHECK(d[3] == 4); + CHECK(d[4] == 5); + CHECK(d[5] == 6); + CHECK(d[6] == 1); + CHECK(d[7] == 1); + CHECK(d[8] == 1); + CHECK(d[9] == 1); + CHECK(d[10] == 1); + CHECK(d[11] == 1); + CHECK(d[12] == 1); + CHECK(d[13] == 1); + CHECK_THROWS(d.insert(d.cbegin(), {2, 3, 4, 5}), std::bad_alloc); + } + } + + { // insert iter iter + { + vector v(100); + int a[] = {1, 2, 3, 4, 5}; + const std::size_t n = sizeof(a) / sizeof(a[0]); + vector::iterator i = + v.insert(v.cbegin() + 10, (a + 0), (a + n)); + CHECK(v.size() == 100 + n); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + for (std::size_t k = 0; k < n; ++j, ++k) { + CHECK(v[j] == a[k]); + } + for (; j < 105; ++j) { + CHECK(v[j] == 0); + } + } + { + vector v(100); + size_t sz = v.size(); + int a[] = {1, 2, 3, 4, 5}; + const unsigned n = sizeof(a) / sizeof(a[0]); + vector::iterator i = + v.insert(v.cbegin() + 10, (a + 0), (a + n)); + CHECK(v.size() == sz + n); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + for (std::size_t k = 0; k < n; ++j, ++k) { + CHECK(v[j] == a[k]); + } + for (; j < v.size(); ++j) { + CHECK(v[j] == 0); + } + } + } + + { // insert iter rvalue + { + vector v(100); + vector::iterator i = v.insert(v.cbegin() + 10, moint(3)); + CHECK(v.size() == 101); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == moint()); + } + CHECK(v[j] == moint(3)); + for (++j; j < 101; ++j) { + CHECK(v[j] == moint()); + } + } + } + + { // insert iter size + { + vector v(100); + vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); + CHECK(v.size() == 105); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + for (; j < 15; ++j) { + CHECK(v[j] == 1); + } + for (++j; j < 105; ++j) { + CHECK(v[j] == 0); + } + } + { + vector v(100); + size_t sz = v.size(); + vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); + CHECK(v.size() == sz + 5); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + for (; j < 15; ++j) { + CHECK(v[j] == 1); + } + for (++j; j < v.size(); ++j) { + CHECK(v[j] == 0); + } + } + { + vector v(100); + size_t sz = v.size(); + vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); + CHECK(v.size() == sz + 5); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + for (; j < 15; ++j) { + CHECK(v[j] == 1); + } + for (++j; j < v.size(); ++j) { + CHECK(v[j] == 0); + } + } + } + + { // iter value: + { + vector v(100); + vector::iterator i = v.insert(v.cbegin() + 10, 1); + CHECK(v.size() == 101); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + CHECK(v[j] == 1); + for (++j; j < 101; ++j) { + CHECK(v[j] == 0); + } + } + { + vector v(100); + size_t sz = v.size(); + vector::iterator i = v.insert(v.cbegin() + 10, 1); + CHECK(v.size() == sz + 1); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + CHECK(v[j] == 1); + for (++j; j < v.size(); ++j) { + CHECK(v[j] == 0); + } + } + { + vector v(100); + v.pop_back(); + v.pop_back(); // force no reallocation + size_t sz = v.size(); + vector::iterator i = v.insert(v.cbegin() + 10, 1); + CHECK(v.size() == sz + 1); + CHECK(i == v.begin() + 10); + std::size_t j; + for (j = 0; j < 10; ++j) { + CHECK(v[j] == 0); + } + CHECK(v[j] == 1); + for (++j; j < v.size(); ++j) { + CHECK(v[j] == 0); + } + } + } + + { // push back move only + { + vector c; + c.push_back(moint(0)); + CHECK(c.size() == 1); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + c.push_back(moint(1)); + CHECK(c.size() == 2); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + c.push_back(moint(2)); + CHECK(c.size() == 3); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + c.push_back(moint(3)); + CHECK(c.size() == 4); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + c.push_back(moint(4)); + CHECK(c.size() == 5); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + } + { + vector c; + auto i = c.try_push_back(moint(0)); + CHECK(c.size() == 1); + CHECK((uintptr_t)(c.begin() + 0) == (uintptr_t)i); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = c.try_push_back(moint(1)); + CHECK(c.size() == 2); + CHECK((uintptr_t)(c.begin() + 1) == (uintptr_t)i); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = c.try_push_back(moint(2)); + CHECK((uintptr_t)(c.begin() + 2) == (uintptr_t)i); + CHECK(c.size() == 3); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = c.try_push_back(moint(3)); + CHECK((uintptr_t)(c.begin() + 3) == (uintptr_t)i); + CHECK(c.size() == 4); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = c.try_push_back(moint(4)); + CHECK((uintptr_t)(c.begin() + 4) == (uintptr_t)i); + CHECK(c.size() == 5); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + } + { + vector c; + auto i = &c.unchecked_push_back(moint(0)); + CHECK(c.size() == 1); + CHECK((uintptr_t)(c.begin() + 0) == (uintptr_t)i); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = &c.unchecked_push_back(moint(1)); + CHECK(c.size() == 2); + CHECK((uintptr_t)(c.begin() + 1) == (uintptr_t)i); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = &c.unchecked_push_back(moint(2)); + CHECK((uintptr_t)(c.begin() + 2) == (uintptr_t)i); + CHECK(c.size() == 3); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = &c.unchecked_push_back(moint(3)); + CHECK((uintptr_t)(c.begin() + 3) == (uintptr_t)i); + CHECK(c.size() == 4); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + i = &c.unchecked_push_back(moint(4)); + CHECK((uintptr_t)(c.begin() + 4) == (uintptr_t)i); + CHECK(c.size() == 5); + for (std::size_t j = 0; j < c.size(); ++j) { + CHECK(c[j] == moint(j)); + } + } + } + + std::cerr << "TESTS PASSED" << std::endl; + return 0; +}