diff --git a/CMakeLists.txt b/CMakeLists.txt index e58de23b..a19ff29a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,10 +203,12 @@ set(SPARROW_HEADERS ${SPARROW_INCLUDE_DIR}/sparrow/layout/struct_layout/struct_value.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/date_array.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/duration_array.hpp + ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/interval_types.hpp + ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/time_array.hpp + ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/time_types.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/timestamp_array.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/timestamp_concepts.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/timestamp_reference.hpp - ${SPARROW_INCLUDE_DIR}/sparrow/layout/temporal/interval_types.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/trivial_copyable_data_access.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/variable_size_binary_layout/variable_size_binary_array.hpp ${SPARROW_INCLUDE_DIR}/sparrow/layout/variable_size_binary_layout/variable_size_binary_iterator.hpp diff --git a/include/sparrow/arrow_interface/arrow_array_schema_info_utils.hpp b/include/sparrow/arrow_interface/arrow_array_schema_info_utils.hpp index 34562f08..965ce9c3 100644 --- a/include/sparrow/arrow_interface/arrow_array_schema_info_utils.hpp +++ b/include/sparrow/arrow_interface/arrow_array_schema_info_utils.hpp @@ -50,6 +50,10 @@ namespace sparrow case data_type::TIMESTAMP_MILLISECONDS: case data_type::TIMESTAMP_MICROSECONDS: case data_type::TIMESTAMP_NANOSECONDS: + case data_type::TIME_SECONDS: + case data_type::TIME_MILLISECONDS: + case data_type::TIME_MICROSECONDS: + case data_type::TIME_NANOSECONDS: case data_type::DURATION_SECONDS: case data_type::DURATION_MILLISECONDS: case data_type::DURATION_MICROSECONDS: diff --git a/include/sparrow/builder/builder.hpp b/include/sparrow/builder/builder.hpp index 740957d1..7950b76a 100644 --- a/include/sparrow/builder/builder.hpp +++ b/include/sparrow/builder/builder.hpp @@ -22,21 +22,21 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "sparrow/array.hpp" +#include "sparrow/builder/builder_utils.hpp" +#include "sparrow/builder/nested_eq.hpp" +#include "sparrow/builder/nested_less.hpp" +#include "sparrow/layout/dictionary_encoded_array.hpp" +#include "sparrow/layout/fixed_width_binary_array.hpp" +#include "sparrow/layout/list_layout/list_array.hpp" +#include "sparrow/layout/primitive_array.hpp" +#include "sparrow/layout/struct_layout/struct_array.hpp" +#include "sparrow/layout/temporal/date_array.hpp" #include "sparrow/layout/temporal/interval_array.hpp" +#include "sparrow/layout/temporal/time_array.hpp" +#include "sparrow/layout/union_array.hpp" +#include "sparrow/layout/variable_size_binary_layout/variable_size_binary_array.hpp" +#include "sparrow/utils/ranges.hpp" namespace sparrow { @@ -144,6 +144,13 @@ namespace sparrow mpl::predicate::same_as>{} ); + template + concept translates_to_time_layout = std::ranges::input_range + && mpl::any_of( + time_types_t{}, + mpl::predicate::same_as>{} + ); + template concept translate_to_variable_sized_list_layout = std::ranges::input_range && std::ranges::input_range> @@ -264,6 +271,18 @@ namespace sparrow } }; + template + struct builder + { + using type = sparrow::time_array>; + + template + static type create(U&& t) + { + return type(std::forward(t)); + } + }; + template struct builder { diff --git a/include/sparrow/layout/dispatch.hpp b/include/sparrow/layout/dispatch.hpp index 30b16d2d..cd431ded 100644 --- a/include/sparrow/layout/dispatch.hpp +++ b/include/sparrow/layout/dispatch.hpp @@ -29,6 +29,7 @@ #include "sparrow/layout/temporal/date_array.hpp" #include "sparrow/layout/temporal/duration_array.hpp" #include "sparrow/layout/temporal/interval_array.hpp" +#include "sparrow/layout/temporal/time_array.hpp" #include "sparrow/layout/temporal/timestamp_array.hpp" #include "sparrow/layout/union_array.hpp" #include "sparrow/layout/variable_size_binary_layout/variable_size_binary_array.hpp" @@ -145,6 +146,14 @@ namespace sparrow return func(unwrap_array>>(ar)); case data_type::TIMESTAMP_NANOSECONDS: return func(unwrap_array>>(ar)); + case data_type::TIME_SECONDS: + return func(unwrap_array(ar)); + case data_type::TIME_MILLISECONDS: + return func(unwrap_array(ar)); + case data_type::TIME_MICROSECONDS: + return func(unwrap_array(ar)); + case data_type::TIME_NANOSECONDS: + return func(unwrap_array(ar)); case data_type::DURATION_SECONDS: return func(unwrap_array(ar)); case data_type::DURATION_MILLISECONDS: diff --git a/include/sparrow/layout/temporal/time_array.hpp b/include/sparrow/layout/temporal/time_array.hpp new file mode 100644 index 00000000..f6cb246b --- /dev/null +++ b/include/sparrow/layout/temporal/time_array.hpp @@ -0,0 +1,66 @@ +// Copyright 2024 Man Group Operations Limited +// +// 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. + +#pragma once + +#include "sparrow/layout/array_trivial_copyable.hpp" +#include "sparrow/layout/temporal/time_types.hpp" + +// tts : std::chrono::seconds + +namespace sparrow +{ + using time_types_t = mpl:: + typelist; + + static constexpr time_types_t time_types; + template + concept time_type = mpl::contains(time_types); + + /** + * Array of time values. + * + * As the other arrays in sparrow, \c time_array provides an API as if it was holding + * \c nullable values instead of \c T values. + * + * Internally, the array contains a validity bitmap and a contiguous memory buffer + * holding the values. + * + * @tparam T the type of the values in the array. + * @see https://arrow.apache.org/docs/dev/format/Columnar.html#fixed-size-primitive-layout + */ + template + using time_array = array_trivial_copyable; + + using time_seconds_array = time_array; + using time_milliseconds_array = time_array; + using time_microseconds_array = time_array; + using time_nanoseconds_array = time_array; + + template + struct is_time_array : std::false_type + { + }; + + template + struct is_time_array> : std::true_type + { + }; + + /** + * Checks whether T is a time_array type. + */ + template + constexpr bool is_time_array_v = is_time_array::value; +} diff --git a/include/sparrow/layout/temporal/time_types.hpp b/include/sparrow/layout/temporal/time_types.hpp new file mode 100644 index 00000000..b533c707 --- /dev/null +++ b/include/sparrow/layout/temporal/time_types.hpp @@ -0,0 +1,99 @@ +// Copyright 2024 Man Group Operations Limited +// +// 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. + +#pragma once + +#include +#include + +#if defined(__cpp_lib_format) +# include +#endif + +namespace sparrow::chrono +{ + /** + * A duration representing time elapsed since midnight. + */ + struct time_seconds : public std::chrono::duration + { + time_seconds() = default; + + explicit time_seconds(int32_t seconds) + : std::chrono::duration(seconds) + { + } + }; + + /** + * A duration representing time elapsed since midnight, in milliseconds. + */ + struct time_milliseconds : public std::chrono::duration + { + time_milliseconds() = default; + + explicit time_milliseconds(int32_t milliseconds) + : std::chrono::duration(milliseconds) + { + } + }; + + /** + * A duration representing time elapsed since midnight, in microseconds. + */ + struct time_microseconds : public std::chrono::duration + { + time_microseconds() = default; + + explicit time_microseconds(int64_t microseconds) + : std::chrono::duration(microseconds) + { + } + }; + + /** + * A duration representing time elapsed since midnight, in nanoseconds. + */ + struct time_nanoseconds : public std::chrono::duration + { + time_nanoseconds() = default; + + explicit time_nanoseconds(int64_t nanoseconds) + : std::chrono::duration(nanoseconds) + { + } + }; +} + +#if defined(__cpp_lib_format) + +template + requires std::same_as + || std::same_as + || std::same_as + || std::same_as +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); // Simple implementation + } + + auto format(const T& time, std::format_context& ctx) const + { + return std::format_to(ctx.out(), "{}", time.count()); + } +}; + +#endif diff --git a/include/sparrow/types/data_traits.hpp b/include/sparrow/types/data_traits.hpp index 192df218..89530df9 100644 --- a/include/sparrow/types/data_traits.hpp +++ b/include/sparrow/types/data_traits.hpp @@ -19,6 +19,7 @@ #include "sparrow/layout/temporal/date_array.hpp" #include "sparrow/layout/temporal/interval_types.hpp" +#include "sparrow/layout/temporal/time_types.hpp" #include "sparrow/types/data_type.hpp" #include "sparrow/utils/nullable.hpp" #include "sparrow/utils/vector_view.hpp" @@ -181,6 +182,30 @@ namespace sparrow using const_reference = timestamp; }; + template <> + struct arrow_traits : common_native_types_traits + { + static constexpr data_type type_id = data_type::TIME_SECONDS; + }; + + template <> + struct arrow_traits : common_native_types_traits + { + static constexpr data_type type_id = data_type::TIME_MILLISECONDS; + }; + + template <> + struct arrow_traits : common_native_types_traits + { + static constexpr data_type type_id = data_type::TIME_MICROSECONDS; + }; + + template <> + struct arrow_traits : common_native_types_traits + { + static constexpr data_type type_id = data_type::TIME_NANOSECONDS; + }; + template <> struct arrow_traits : common_native_types_traits { diff --git a/include/sparrow/types/data_type.hpp b/include/sparrow/types/data_type.hpp index 1ced357f..3f7b3ca0 100644 --- a/include/sparrow/types/data_type.hpp +++ b/include/sparrow/types/data_type.hpp @@ -19,6 +19,7 @@ #include "sparrow/layout/temporal/date_types.hpp" #include "sparrow/layout/temporal/interval_types.hpp" +#include "sparrow/layout/temporal/time_types.hpp" #if defined(SPARROW_USE_DATE_POLYFILL) @@ -195,6 +196,10 @@ namespace sparrow TIMESTAMP_MILLISECONDS, TIMESTAMP_MICROSECONDS, TIMESTAMP_NANOSECONDS, + TIME_SECONDS, + TIME_MILLISECONDS, + TIME_MICROSECONDS, + TIME_NANOSECONDS, DURATION_SECONDS, DURATION_MILLISECONDS, DURATION_MICROSECONDS, @@ -333,6 +338,22 @@ namespace sparrow { return data_type::INTERVAL_MONTHS_DAYS_NANOSECONDS; } + else if (format == "tts") + { + return data_type::TIME_SECONDS; + } + else if (format == "ttm") + { + return data_type::TIME_MILLISECONDS; + } + else if (format == "ttu") + { + return data_type::TIME_MICROSECONDS; + } + else if (format == "ttn") + { + return data_type::TIME_NANOSECONDS; + } } else if (format == "+l") { @@ -537,6 +558,14 @@ namespace sparrow return "tiD"; case data_type::INTERVAL_MONTHS_DAYS_NANOSECONDS: return "tin"; + case data_type::TIME_SECONDS: + return "tts"; + case data_type::TIME_MILLISECONDS: + return "ttm"; + case data_type::TIME_MICROSECONDS: + return "ttu"; + case data_type::TIME_NANOSECONDS: + return "ttn"; case data_type::LIST: return "+l"; case data_type::LARGE_LIST: @@ -623,6 +652,10 @@ namespace sparrow chrono::months, days_time_interval, month_day_nanoseconds_interval, + chrono::time_seconds, + chrono::time_milliseconds, + chrono::time_microseconds, + chrono::time_nanoseconds, // TODO: add missing fundamental types here list_value, struct_value, @@ -879,6 +912,14 @@ namespace std return "Interval days time"; case INTERVAL_MONTHS_DAYS_NANOSECONDS: return "Interval months days nanoseconds"; + case TIME_SECONDS: + return "Time seconds"; + case TIME_MILLISECONDS: + return "Time milliseconds"; + case TIME_MICROSECONDS: + return "Time microseconds"; + case TIME_NANOSECONDS: + return "Time nanoseconds"; case LIST: return "List"; case LARGE_LIST: diff --git a/src/array_factory.cpp b/src/array_factory.cpp index 29d4b359..fc969558 100644 --- a/src/array_factory.cpp +++ b/src/array_factory.cpp @@ -25,6 +25,7 @@ #include "sparrow/layout/temporal/date_array.hpp" #include "sparrow/layout/temporal/duration_array.hpp" #include "sparrow/layout/temporal/interval_array.hpp" +#include "sparrow/layout/temporal/time_array.hpp" #include "sparrow/layout/temporal/timestamp_array.hpp" #include "sparrow/layout/union_array.hpp" #include "sparrow/layout/variable_size_binary_layout/variable_size_binary_array.hpp" @@ -150,6 +151,14 @@ namespace sparrow return detail::make_wrapper_ptr(std::move(proxy)); case data_type::INTERVAL_MONTHS_DAYS_NANOSECONDS: return detail::make_wrapper_ptr(std::move(proxy)); + case data_type::TIME_SECONDS: + return detail::make_wrapper_ptr(std::move(proxy)); + case data_type::TIME_MILLISECONDS: + return detail::make_wrapper_ptr(std::move(proxy)); + case data_type::TIME_MICROSECONDS: + return detail::make_wrapper_ptr(std::move(proxy)); + case data_type::TIME_NANOSECONDS: + return detail::make_wrapper_ptr(std::move(proxy)); case data_type::MAP: case data_type::DECIMAL32: return detail::make_wrapper_ptr(std::move(proxy)); diff --git a/src/arrow_interface/arrow_array.cpp b/src/arrow_interface/arrow_array.cpp index e075f721..7f019f32 100644 --- a/src/arrow_interface/arrow_array.cpp +++ b/src/arrow_interface/arrow_array.cpp @@ -142,6 +142,12 @@ namespace sparrow return {make_valid_buffer(), make_buffer(1, size * 8)}; case sparrow::data_type::INTERVAL_MONTHS_DAYS_NANOSECONDS: return {make_valid_buffer(), make_buffer(1, size * 16)}; + case sparrow::data_type::TIME_SECONDS: + case sparrow::data_type::TIME_MILLISECONDS: + return {make_valid_buffer(), make_buffer(1, size * 4)}; + case sparrow::data_type::TIME_MICROSECONDS: + case sparrow::data_type::TIME_NANOSECONDS: + return {make_valid_buffer(), make_buffer(1, size * 8)}; case data_type::DECIMAL32: return {make_valid_buffer(), make_buffer(1, size * 4)}; case data_type::DECIMAL64: diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 11f7e727..313e16d8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -154,6 +154,7 @@ else() test_run_end_encoded_array.cpp test_string_array.cpp test_struct_array.cpp + test_time_array.cpp test_timestamp_array.cpp test_traits.cpp test_union_array.cpp diff --git a/test/test_time_array.cpp b/test/test_time_array.cpp new file mode 100644 index 00000000..b4c7a830 --- /dev/null +++ b/test/test_time_array.cpp @@ -0,0 +1,563 @@ +// Copyright 2024 Man Group Operations Limited +// +// 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. + +#include "sparrow/layout/temporal/time_array.hpp" +#include "sparrow/types/data_traits.hpp" + +#include "doctest/doctest.h" + +namespace sparrow +{ + using testing_types = mpl::rename; + + template + std::vector> make_nullable_values(size_t count) + { + std::vector> values; + values.reserve(count); + for (size_t i = 0; i < count; ++i) + { + values.push_back(nullable(T(static_cast(i)))); + } + return values; + } + + TEST_SUITE("time_array") + { + TEST_CASE_TEMPLATE_DEFINE("", T, time_array_id) + { + const auto input_values = make_nullable_values(10); + SUBCASE("constructors") + { + SUBCASE("with range") + { + time_array ar(input_values); + CHECK_EQ(ar.size(), input_values.size()); + } + + SUBCASE("copy") + { + const time_array ar(input_values); + const time_array ar2(ar); + CHECK_EQ(ar, ar2); + } + + SUBCASE("move") + { + time_array ar(input_values); + const time_array ar2(std::move(ar)); + CHECK_EQ(ar2.size(), input_values.size()); + } + } + + SUBCASE("operator[]") + { + SUBCASE("const") + { + const time_array ar(input_values); + for (size_t i = 0; i < ar.size(); ++i) + { + const auto ari = ar[i]; + const auto input_values_i = input_values[i]; + CHECK_EQ(ari, input_values_i); + } + } + + SUBCASE("mutable") + { + time_array ar(input_values); + + std::vector> new_values = [&input_values]() + { + std::vector> values; + values.reserve(input_values.size()); + for (size_t i = 0; i < input_values.size(); ++i) + { + if constexpr (std::is_same_v) + { + values.push_back(nullable(T(i + 5))); + } + else if constexpr (std::is_same_v) + { + values.push_back( + nullable(T{std::chrono::days(i + 5), std::chrono::milliseconds(i + 5)}) + ); + } + else if constexpr (std::is_same_v) + { + values.push_back(nullable( + T{std::chrono::months(i + 5), + std::chrono::days(i + 5), + std::chrono::nanoseconds(i + 5)} + )); + } + } + return values; + }(); + + for (size_t i = 0; i < ar.size(); ++i) + { + ar[i] = new_values[i]; + } + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], new_values[i]); + } + } + } + + SUBCASE("front") + { + SUBCASE("const") + { + const time_array ar(input_values); + CHECK_EQ(ar.front(), input_values.front()); + } + } + + SUBCASE("back") + { + SUBCASE("const") + { + const time_array ar(input_values); + CHECK_EQ(ar.back(), input_values.back()); + } + } + + SUBCASE("value_iterator") + { + time_array ar(input_values); + auto ar_values = ar.values(); + SUBCASE("ordering") + { + auto iter = ar_values.begin(); + CHECK(iter < ar_values.end()); + } + + SUBCASE("equality") + { + auto iter = ar_values.begin(); + for (size_t i = 0; i < ar_values.size(); ++i) + { + CHECK_EQ(*iter, input_values[i].get()); + ++iter; + } + CHECK_EQ(iter, ar_values.end()); + } + } + + SUBCASE("const_value_iterator") + { + time_array ar(input_values); + auto ar_values = ar.values(); + + SUBCASE("ordering") + { + const auto citer = ar_values.begin(); + CHECK(citer < ar_values.end()); + } + + SUBCASE("equality") + { + auto citer = ar_values.begin(); + for (size_t i = 0; i < ar_values.size(); ++i) + { + CHECK_EQ(*citer, input_values[i].get()); + ++citer; + } + CHECK_EQ(citer, ar_values.end()); + } + } + + SUBCASE("iterator") + { + time_array ar(input_values); + auto it = ar.begin(); + const auto end = ar.end(); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(*it, input_values[i]); + ++it; + } + CHECK_EQ(it, end); + } + + SUBCASE("const iterator") + { + const time_array ar(input_values); + auto it = ar.cbegin(); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(*it, input_values[i]); + ++it; + } + CHECK_EQ(it, ar.cend()); + } + + SUBCASE("reverse_iterator") + { + time_array ar(input_values); + auto it = ar.rbegin(); + CHECK_EQ(*it, *(ar.end() - 1)); + for (size_t i = 0; i < ar.size(); ++i) + { + const auto idx = ar.size() - 1 - i; + CHECK_EQ(*it, input_values[idx]); + ++it; + } + CHECK_EQ(it, ar.rend()); + } + + const auto new_value = nullable(T(99)); + + SUBCASE("resize") + { + time_array ar(input_values); + const size_t new_size = ar.size() + 2; + ar.resize(ar.size() + 2, new_value); + REQUIRE_EQ(ar.size(), new_size); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[input_values.size()], new_value); + CHECK_EQ(ar[input_values.size() + 1], new_value); + } + + SUBCASE("insert") + { + SUBCASE("with pos and value") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + auto pos = ar.cbegin(); + ar.insert(pos, new_value); + CHECK_EQ(ar[0], new_value); + for (size_t i = 0; i < ar.size() - 1; ++i) + { + CHECK_EQ(ar[i + 1], input_values[i]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.insert(pos, new_value); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[idx], new_value); + for (size_t i = idx; i < ar.size() - 1; ++i) + { + CHECK_EQ(ar[i + 1], input_values[i]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + auto pos = ar.cend(); + ar.insert(pos, new_value); + for (size_t i = 0; i < ar.size() - 1; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[ar.size() - 1], new_value); + } + } + + SUBCASE("with pos, count and value") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + auto pos = ar.cbegin(); + ar.insert(pos, new_value, 2); + CHECK_EQ(ar[0], new_value); + CHECK_EQ(ar[1], new_value); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.insert(pos, new_value, 2); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[idx], new_value); + CHECK_EQ(ar[idx + 1], new_value); + for (size_t i = idx; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + auto pos = ar.cend(); + ar.insert(pos, new_value, 2); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[ar.size() - 2], new_value); + CHECK_EQ(ar[ar.size() - 1], new_value); + } + } + + SUBCASE("with pos and range") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + std::vector> new_values = {new_value, new_value}; + auto pos = ar.cbegin(); + ar.insert(pos, new_values); + CHECK_EQ(ar[0], new_value); + CHECK_EQ(ar[1], new_value); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const std::vector> new_values = {new_value, new_value}; + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.insert(pos, new_values); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[idx], new_value); + CHECK_EQ(ar[idx + 1], new_value); + for (size_t i = idx; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + std::vector> new_values = {new_value, new_value}; + auto pos = ar.cend(); + ar.insert(pos, new_values); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[ar.size() - 2], new_value); + CHECK_EQ(ar[ar.size() - 1], new_value); + } + } + + SUBCASE("with pos and initializer list") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + auto pos = ar.cbegin(); + ar.insert(pos, {new_value, new_value}); + CHECK_EQ(ar[0], new_value); + CHECK_EQ(ar[1], new_value); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.insert(pos, {new_value, new_value}); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[idx], new_value); + CHECK_EQ(ar[idx + 1], new_value); + for (size_t i = idx; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i + 2], input_values[i]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + auto pos = ar.cend(); + ar.insert(pos, {new_value, new_value}); + for (size_t i = 0; i < ar.size() - 2; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + CHECK_EQ(ar[ar.size() - 2], new_value); + CHECK_EQ(ar[ar.size() - 1], new_value); + } + } + } + + SUBCASE("erase") + { + SUBCASE("with pos") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + auto pos = ar.cbegin(); + ar.erase(pos); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i + 1]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.erase(pos); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + for (size_t i = idx; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i + 1]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + auto pos = ar.cend() - 1; + ar.erase(pos); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + } + } + + SUBCASE("with iterators") + { + SUBCASE("at the beginning") + { + time_array ar(input_values); + auto pos = ar.cbegin(); + ar.erase(pos, pos + 2); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i + 2]); + } + } + + SUBCASE("in the middle") + { + time_array ar(input_values); + const size_t idx = input_values.size() / 2; + auto pos = sparrow::next(ar.cbegin(), idx); + ar.erase(pos, pos + 2); + for (size_t i = 0; i < idx; ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + for (size_t i = idx; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i + 2]); + } + } + + SUBCASE("at the end") + { + time_array ar(input_values); + auto pos = ar.cend() - 2; + ar.erase(pos, ar.cend()); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + } + } + } + + SUBCASE("push_back") + { + time_array ar(input_values); + ar.push_back(new_value); + CHECK_EQ(ar.size(), input_values.size() + 1); + CHECK_EQ(ar[ar.size() - 1], new_value); + } + + SUBCASE("pop_back") + { + time_array ar(input_values); + ar.pop_back(); + CHECK_EQ(ar.size(), input_values.size() - 1); + for (size_t i = 0; i < ar.size(); ++i) + { + CHECK_EQ(ar[i], input_values[i]); + } + } + +#if defined(__cpp_lib_format) + SUBCASE("format") + { + const time_array ar(input_values); + const std::string formatted = std::format("{}", ar); + const std::string expected = []() + { + if constexpr (std::is_same_v) + { + return "Time seconds [name=nullptr | size=10] <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>"; + } + else if constexpr (std::is_same_v) + { + return "Time milliseconds [name=nullptr | size=10] <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>"; + } + else if constexpr (std::is_same_v) + { + return "Time microseconds [name=nullptr | size=10] <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>"; + } + else if constexpr (std::is_same_v) + { + return "Time nanoseconds [name=nullptr | size=10] <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>"; + } + }(); + CHECK_EQ(formatted, expected); + } +#endif + } + TEST_CASE_TEMPLATE_APPLY(time_array_id, testing_types); + } +}