llvm/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SUPPORT_INSERT_RANGE_SEQUENCE_CONTAINERS_H
#define SUPPORT_INSERT_RANGE_SEQUENCE_CONTAINERS_H

#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <ranges>
#include <type_traits>
#include <vector>

#include "../exception_safety_helpers.h"
#include "../from_range_helpers.h"
#include "../insert_range_helpers.h"
#include "MoveOnly.h"
#include "almost_satisfies_types.h"
#include "count_new.h"
#include "min_allocator.h"
#include "test_allocator.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"

template <class Container, class Range>
concept HasInsertRange = requires (Container& c, Range&& range) {
  c.insert_range(c.end(), range);
};

template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_insert_range() {
  // Input range with the same value type.
  static_assert(HasInsertRange<Container<T>, InputRange<T>>);
  // Input range with a convertible value type.
  static_assert(HasInsertRange<Container<T>, InputRange<U>>);
  // Input range with a non-convertible value type.
  static_assert(!HasInsertRange<Container<T>, InputRange<Empty>>);
  // Not an input range.
  static_assert(!HasInsertRange<Container<T>, InputRangeNotDerivedFrom>);
  static_assert(!HasInsertRange<Container<T>, InputRangeNotIndirectlyReadable>);
  static_assert(!HasInsertRange<Container<T>, InputRangeNotInputOrOutputIterator>);

  return true;
}

template <class Container, class Range>
concept HasAppendRange = requires (Container& c, Range&& range) {
  c.append_range(range);
};

template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_append_range() {
  // Input range with the same value type.
  static_assert(HasAppendRange<Container<T>, InputRange<T>>);
  // Input range with a convertible value type.
  static_assert(HasAppendRange<Container<T>, InputRange<U>>);
  // Input range with a non-convertible value type.
  static_assert(!HasAppendRange<Container<T>, InputRange<Empty>>);
  // Not an input range.
  static_assert(!HasAppendRange<Container<T>, InputRangeNotDerivedFrom>);
  static_assert(!HasAppendRange<Container<T>, InputRangeNotIndirectlyReadable>);
  static_assert(!HasAppendRange<Container<T>, InputRangeNotInputOrOutputIterator>);

  return true;
}

template <class Container, class Range>
concept HasPrependRange = requires (Container& c, Range&& range) {
  c.prepend_range(range);
};

template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_prepend_range() {
  // Input range with the same value type.
  static_assert(HasPrependRange<Container<T>, InputRange<T>>);
  // Input range with a convertible value type.
  static_assert(HasPrependRange<Container<T>, InputRange<U>>);
  // Input range with a non-convertible value type.
  static_assert(!HasPrependRange<Container<T>, InputRange<Empty>>);
  // Not an input range.
  static_assert(!HasPrependRange<Container<T>, InputRangeNotDerivedFrom>);
  static_assert(!HasPrependRange<Container<T>, InputRangeNotIndirectlyReadable>);
  static_assert(!HasPrependRange<Container<T>, InputRangeNotInputOrOutputIterator>);

  return true;
}

template <class Container, class Range>
concept HasAssignRange = requires (Container& c, Range&& range) {
  c.assign_range(range);
};

template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_assign_range() {
  // Input range with the same value type.
  static_assert(HasAssignRange<Container<T>, InputRange<T>>);
  // Input range with a convertible value type.
  static_assert(HasAssignRange<Container<T>, InputRange<U>>);
  // Input range with a non-convertible value type.
  static_assert(!HasAssignRange<Container<T>, InputRange<Empty>>);
  // Not an input range.
  static_assert(!HasAssignRange<Container<T>, InputRangeNotDerivedFrom>);
  static_assert(!HasAssignRange<Container<T>, InputRangeNotIndirectlyReadable>);
  static_assert(!HasAssignRange<Container<T>, InputRangeNotInputOrOutputIterator>);

  return true;
}

// Empty container.

template <class T>
TestCase<T> constexpr EmptyContainer_EmptyRange {
  .initial = {}, .index = 0, .input = {}, .expected = {}
};
// Note: specializations for `bool` still use `vector<int>` for inputs. This is to avoid dealing with `vector<bool>` and
// its iterators over proxy types.
template <> constexpr TestCase<int> EmptyContainer_EmptyRange<bool> {
  .initial = {}, .index = 0, .input = {}, .expected = {}
};

template <class T> constexpr TestCase<T> EmptyContainer_OneElementRange;
template <> constexpr TestCase<int> EmptyContainer_OneElementRange<int> {
  .initial = {}, .index = 0, .input = {5}, .expected = {5}
};
template <> constexpr TestCase<char> EmptyContainer_OneElementRange<char> {
  .initial = {}, .index = 0, .input = "a", .expected = "a"
};
template <> constexpr TestCase<int> EmptyContainer_OneElementRange<bool> {
  .initial = {}, .index = 0, .input = {true}, .expected = {true}
};

template <class T> constexpr TestCase<T> EmptyContainer_MidRange;
template <> constexpr TestCase<int> EmptyContainer_MidRange<int> {
  .initial = {}, .index = 0, .input = {5, 3, 1, 7, 9}, .expected = {5, 3, 1, 7, 9}
};
template <> constexpr TestCase<char> EmptyContainer_MidRange<char> {
  .initial = {}, .index = 0, .input = "aeiou", .expected = "aeiou"
};
template <> constexpr TestCase<int> EmptyContainer_MidRange<bool> {
  .initial = {}, .index = 0, .input = {1, 1, 0, 1, 1}, .expected = {1, 1, 0, 1, 1}
};

// One-element container.

template <class T> constexpr TestCase<T> OneElementContainer_Begin_EmptyRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_EmptyRange<int> {
  .initial = {3}, .index = 0, .input = {}, .expected = {3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_EmptyRange<char> {
  .initial = "B", .index = 0, .input = {}, .expected = "B"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_EmptyRange<bool> {
  .initial = {0}, .index = 0, .input = {}, .expected = {0}
};

template <class T> constexpr TestCase<T> OneElementContainer_End_EmptyRange;
template <> constexpr TestCase<int> OneElementContainer_End_EmptyRange<int> {
  .initial = {3}, .index = 1, .input = {}, .expected = {3}
};
template <> constexpr TestCase<char> OneElementContainer_End_EmptyRange<char> {
  .initial = "B", .index = 1, .input = {}, .expected = "B"
};
template <> constexpr TestCase<int> OneElementContainer_End_EmptyRange<bool> {
  .initial = {0}, .index = 1, .input = {}, .expected = {0}
};

template <class T> constexpr TestCase<T> OneElementContainer_Begin_OneElementRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_OneElementRange<int> {
  .initial = {3}, .index = 0, .input = {-5}, .expected = {-5, 3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_OneElementRange<char> {
  .initial = "B", .index = 0, .input = "a", .expected = "aB"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_OneElementRange<bool> {
  .initial = {0}, .index = 0, .input = {1}, .expected = {1, 0}
};

template <class T> constexpr TestCase<T> OneElementContainer_End_OneElementRange;
template <> constexpr TestCase<int> OneElementContainer_End_OneElementRange<int> {
  .initial = {3}, .index = 1, .input = {-5}, .expected = {3, -5}
};
template <> constexpr TestCase<char> OneElementContainer_End_OneElementRange<char> {
  .initial = "B", .index = 1, .input = "a", .expected = "Ba"
};
template <> constexpr TestCase<int> OneElementContainer_End_OneElementRange<bool> {
  .initial = {0}, .index = 1, .input = {1}, .expected = {0, 1}
};

template <class T> constexpr TestCase<T> OneElementContainer_Begin_MidRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_MidRange<int> {
  .initial = {3}, .index = 0, .input = {-5, -3, -1, -7, -9}, .expected = {-5, -3, -1, -7, -9, 3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_MidRange<char> {
  .initial = "B", .index = 0, .input = "aeiou", .expected = "aeiouB"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_MidRange<bool> {
  .initial = {0}, .index = 0, .input = {1, 1, 0, 1, 1}, .expected = {1, 1, 0, 1, 1, 0}
};

template <class T> constexpr TestCase<T> OneElementContainer_End_MidRange;
template <> constexpr TestCase<int> OneElementContainer_End_MidRange<int> {
  .initial = {3}, .index = 1, .input = {-5, -3, -1, -7, -9}, .expected = {3, -5, -3, -1, -7, -9}
};
template <> constexpr TestCase<char> OneElementContainer_End_MidRange<char> {
  .initial = "B", .index = 1, .input = "aeiou", .expected = "Baeiou"
};
template <> constexpr TestCase<int> OneElementContainer_End_MidRange<bool> {
  .initial = {0}, .index = 1, .input = {1, 1, 0, 1, 1}, .expected = {0, 1, 1, 0, 1, 1}
};

// Full container / empty range.

template <class T> constexpr TestCase<T> FullContainer_Begin_EmptyRange;
template <> constexpr TestCase<int> FullContainer_Begin_EmptyRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 0, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_EmptyRange<char> {
  .initial = "_BCD_", .index = 0, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_EmptyRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 0, .input = {}, .expected = {0, 0, 1, 0, 0}
};

template <class T> constexpr TestCase<T> FullContainer_Mid_EmptyRange;
template <> constexpr TestCase<int> FullContainer_Mid_EmptyRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 2, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_EmptyRange<char> {
  .initial = "_BCD_", .index = 2, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_EmptyRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 2, .input = {}, .expected = {0, 0, 1, 0, 0}
};

template <class T> constexpr TestCase<T> FullContainer_End_EmptyRange;
template <> constexpr TestCase<int> FullContainer_End_EmptyRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 5, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_End_EmptyRange<char> {
  .initial = "_BCD_", .index = 5, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_End_EmptyRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 5, .input = {}, .expected = {0, 0, 1, 0, 0}
};

// Full container / one-element range.

template <class T> constexpr TestCase<T> FullContainer_Begin_OneElementRange;
template <> constexpr TestCase<int> FullContainer_Begin_OneElementRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 0, .input = {-5}, .expected = {-5, 11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_OneElementRange<char> {
  .initial = "_BCD_", .index = 0, .input = "a", .expected = "a_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_OneElementRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 0, .input = {1}, .expected = {1, 0, 0, 1, 0, 0}
};

template <class T> constexpr TestCase<T> FullContainer_Mid_OneElementRange;
template <> constexpr TestCase<int> FullContainer_Mid_OneElementRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 2, .input = {-5}, .expected = {11, 29, -5, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_OneElementRange<char> {
  .initial = "_BCD_", .index = 2, .input = "a", .expected = "_BaCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_OneElementRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 2, .input = {1}, .expected = {0, 0, 1, 1, 0, 0}
};

template <class T> constexpr TestCase<T> FullContainer_End_OneElementRange;
template <> constexpr TestCase<int> FullContainer_End_OneElementRange<int> {
  .initial = {11, 29, 35, 14, 84}, .index = 5, .input = {-5}, .expected = {11, 29, 35, 14, 84, -5}
};
template <> constexpr TestCase<char> FullContainer_End_OneElementRange<char> {
  .initial = "_BCD_", .index = 5, .input = "a", .expected = "_BCD_a"
};
template <> constexpr TestCase<int> FullContainer_End_OneElementRange<bool> {
  .initial = {0, 0, 1, 0, 0}, .index = 5, .input = {1}, .expected = {0, 0, 1, 0, 0, 1}
};

// Full container / mid-sized range.

template <class T> constexpr TestCase<T> FullContainer_Begin_MidRange;
template <> constexpr TestCase<int> FullContainer_Begin_MidRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 0,
  .input = {-5, -3, -1, -7, -9},
  .expected = {-5, -3, -1, -7, -9, 11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_MidRange<char> {
  .initial = "_BCD_",
  .index = 0,
  .input = "aeiou",
  .expected = "aeiou_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_MidRange<bool> {
  .initial = {0, 0, 1, 0, 1},
  .index = 0,
  .input = {1, 1, 0, 1, 1},
  .expected = {1, 1, 0, 1, 1, 0, 0, 1, 0, 1}
};

template <class T> constexpr TestCase<T> FullContainer_Mid_MidRange;
template <> constexpr TestCase<int> FullContainer_Mid_MidRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 2,
  .input = {-5, -3, -1, -7, -9},
  .expected = {11, 29, -5, -3, -1, -7, -9, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_MidRange<char> {
  .initial = "_BCD_",
  .index = 2,
  .input = "aeiou",
  .expected = "_BaeiouCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_MidRange<bool> {
  .initial = {0, 0, 1, 0, 1},
  .index = 2,
  .input = {1, 1, 0, 1, 1},
  .expected = {0, 0, 1, 1, 0, 1, 1, 1, 0, 1}
};

template <class T> constexpr TestCase<T> FullContainer_End_MidRange;
template <> constexpr TestCase<int> FullContainer_End_MidRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 5,
  .input = {-5, -3, -1, -7, -9},
  .expected = {11, 29, 35, 14, 84, -5, -3, -1, -7, -9}
};
template <> constexpr TestCase<char> FullContainer_End_MidRange<char> {
  .initial = "_BCD_",
  .index = 5,
  .input = "aeiou",
  .expected = "_BCD_aeiou"
};
template <> constexpr TestCase<int> FullContainer_End_MidRange<bool> {
  .initial = {0, 0, 1, 0, 1},
  .index = 5,
  .input = {1, 1, 0, 1, 1},
  .expected = {0, 0, 1, 0, 1, 1, 1, 0, 1, 1}
};

// Full container / long range.

template <class T> constexpr TestCase<T> FullContainer_Begin_LongRange;
template <> constexpr TestCase<int> FullContainer_Begin_LongRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 0,
  .input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
  .expected = {
      -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48, 11, 29, 35, 14, 84
  }
};
template <> constexpr TestCase<char> FullContainer_Begin_LongRange<char> {
  .initial = "_BCD_",
  .index = 0,
  .input = "aeiouqwxyz5781964203",
  .expected = "aeiouqwxyz5781964203_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_LongRange<bool> {
  .initial = {0, 0, 1, 0, 0},
  .index = 0,
  .input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
  .expected = {
      1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
  }
};

template <class T> constexpr TestCase<T> FullContainer_Mid_LongRange;
template <> constexpr TestCase<int> FullContainer_Mid_LongRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 2,
  .input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
  .expected = {
      11, 29, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48, 35, 14, 84
  }
};
template <> constexpr TestCase<char> FullContainer_Mid_LongRange<char> {
  .initial = "_BCD_",
  .index = 2,
  .input = "aeiouqwxyz5781964203",
  .expected = "_Baeiouqwxyz5781964203CD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_LongRange<bool> {
  .initial = {0, 0, 1, 0, 0},
  .index = 2,
  .input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
  .expected = {
      0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0
  }
};

template <class T> constexpr TestCase<T> FullContainer_End_LongRange;
template <> constexpr TestCase<int> FullContainer_End_LongRange<int> {
  .initial = {11, 29, 35, 14, 84},
  .index = 5,
  .input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
  .expected = {
      11, 29, 35, 14, 84, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48
  }
};
template <> constexpr TestCase<char> FullContainer_End_LongRange<char> {
  .initial = "_BCD_",
  .index = 5,
  .input = "aeiouqwxyz5781964203",
  .expected = "_BCD_aeiouqwxyz5781964203"
};
template <> constexpr TestCase<int> FullContainer_End_LongRange<bool> {
  .initial = {0, 0, 1, 0, 1},
  .index = 5,
  .input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
  .expected = {
      0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
  }
};

// Sequence containers tests.

template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_insert_range(Validate validate) {
  using T = typename Container::value_type;
  using D      = typename Container::difference_type;
  auto get_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.begin(), static_cast<D>(test_case.index)); };

  auto test = [&](auto& test_case) {
    Container c(test_case.initial.begin(), test_case.initial.end());
    auto in = wrap_input<Iter, Sent>(test_case.input);
    auto pos = get_pos(c, test_case);

    auto result = c.insert_range(pos, in);
    assert(result == get_pos(c, test_case));
    validate(c);
    return std::ranges::equal(c, test_case.expected);
  };

  { // Empty container.
    // empty_c.insert_range(end, empty_range)
    assert(test(EmptyContainer_EmptyRange<T>));
    // empty_c.insert_range(end, one_element_range)
    assert(test(EmptyContainer_OneElementRange<T>));
    // empty_c.insert_range(end, mid_range)
    assert(test(EmptyContainer_MidRange<T>));
  }

  { // One-element container.
    // one_element_c.insert_range(begin, empty_range)
    assert(test(OneElementContainer_Begin_EmptyRange<T>));
    // one_element_c.insert_range(end, empty_range)
    assert(test(OneElementContainer_End_EmptyRange<T>));
    // one_element_c.insert_range(begin, one_element_range)
    assert(test(OneElementContainer_Begin_OneElementRange<T>));
    // one_element_c.insert_range(end, one_element_range)
    assert(test(OneElementContainer_End_OneElementRange<T>));
    // one_element_c.insert_range(begin, mid_range)
    assert(test(OneElementContainer_Begin_MidRange<T>));
    // one_element_c.insert_range(end, mid_range)
    assert(test(OneElementContainer_End_MidRange<T>));
  }

  { // Full container.
    // full_container.insert_range(begin, empty_range)
    assert(test(FullContainer_Begin_EmptyRange<T>));
    // full_container.insert_range(mid, empty_range)
    assert(test(FullContainer_Mid_EmptyRange<T>));
    // full_container.insert_range(end, empty_range)
    assert(test(FullContainer_End_EmptyRange<T>));
    // full_container.insert_range(begin, one_element_range)
    assert(test(FullContainer_Begin_OneElementRange<T>));
    // full_container.insert_range(end, one_element_range)
    assert(test(FullContainer_Mid_OneElementRange<T>));
    // full_container.insert_range(end, one_element_range)
    assert(test(FullContainer_End_OneElementRange<T>));
    // full_container.insert_range(begin, mid_range)
    assert(test(FullContainer_Begin_MidRange<T>));
    // full_container.insert_range(mid, mid_range)
    assert(test(FullContainer_Mid_MidRange<T>));
    // full_container.insert_range(end, mid_range)
    assert(test(FullContainer_End_MidRange<T>));
    // full_container.insert_range(begin, long_range)
    assert(test(FullContainer_Begin_LongRange<T>));
    // full_container.insert_range(mid, long_range)
    assert(test(FullContainer_Mid_LongRange<T>));
    // full_container.insert_range(end, long_range)
    assert(test(FullContainer_End_LongRange<T>));
  }
}

template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_prepend_range(Validate validate) {
  using T = typename Container::value_type;

  auto test = [&](auto& test_case) {
    Container c(test_case.initial.begin(), test_case.initial.end());
    auto in = wrap_input<Iter, Sent>(test_case.input);

    c.prepend_range(in);
    validate(c);
    return std::ranges::equal(c, test_case.expected);
  };

  { // Empty container.
    // empty_c.prepend_range(empty_range)
    assert(test(EmptyContainer_EmptyRange<T>));
    // empty_c.prepend_range(one_element_range)
    assert(test(EmptyContainer_OneElementRange<T>));
    // empty_c.prepend_range(mid_range)
    assert(test(EmptyContainer_MidRange<T>));
  }

  { // One-element container.
    // one_element_c.prepend_range(empty_range)
    assert(test(OneElementContainer_Begin_EmptyRange<T>));
    // one_element_c.prepend_range(one_element_range)
    assert(test(OneElementContainer_Begin_OneElementRange<T>));
    // one_element_c.prepend_range(mid_range)
    assert(test(OneElementContainer_Begin_MidRange<T>));
  }

  { // Full container.
    // full_container.prepend_range(empty_range)
    assert(test(FullContainer_Begin_EmptyRange<T>));
    // full_container.prepend_range(one_element_range)
    assert(test(FullContainer_Begin_OneElementRange<T>));
    // full_container.prepend_range(mid_range)
    assert(test(FullContainer_Begin_MidRange<T>));
    // full_container.prepend_range(long_range)
    assert(test(FullContainer_Begin_LongRange<T>));
  }
}

template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_append_range(Validate validate) {
  using T = typename Container::value_type;

  auto test = [&](auto& test_case) {
    Container c(test_case.initial.begin(), test_case.initial.end());
    auto in = wrap_input<Iter, Sent>(test_case.input);

    c.append_range(in);
    validate(c);
    return std::ranges::equal(c, test_case.expected);
  };

  { // Empty container.
    // empty_c.append_range(empty_range)
    assert(test(EmptyContainer_EmptyRange<T>));
    // empty_c.append_range(one_element_range)
    assert(test(EmptyContainer_OneElementRange<T>));
    // empty_c.append_range(mid_range)
    assert(test(EmptyContainer_MidRange<T>));
  }

  { // One-element container.
    // one_element_c.append_range(empty_range)
    assert(test(OneElementContainer_End_EmptyRange<T>));
    // one_element_c.append_range(one_element_range)
    assert(test(OneElementContainer_End_OneElementRange<T>));
    // one_element_c.append_range(mid_range)
    assert(test(OneElementContainer_End_MidRange<T>));
  }

  { // Full container.
    // full_container.append_range(empty_range)
    assert(test(FullContainer_End_EmptyRange<T>));
    // full_container.append_range(one_element_range)
    assert(test(FullContainer_End_OneElementRange<T>));
    // full_container.append_range(mid_range)
    assert(test(FullContainer_End_MidRange<T>));
    // full_container.append_range(long_range)
    assert(test(FullContainer_End_LongRange<T>));
  }
}

template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_assign_range(Validate validate) {
  using T = typename Container::value_type;

  auto& initial_empty = EmptyContainer_EmptyRange<T>.initial;
  auto& initial_one_element = OneElementContainer_Begin_EmptyRange<T>.initial;
  auto& initial_full = FullContainer_Begin_EmptyRange<T>.initial;
  auto& input_empty = FullContainer_Begin_EmptyRange<T>.input;
  auto& input_one_element = FullContainer_Begin_OneElementRange<T>.input;
  auto& input_mid_range = FullContainer_Begin_MidRange<T>.input;
  auto& input_long_range = FullContainer_Begin_LongRange<T>.input;

  auto test = [&](auto& initial, auto& input) {
    Container c(initial.begin(), initial.end());
    auto in = wrap_input<Iter, Sent>(input);

    c.assign_range(in);
    validate(c);
    return std::ranges::equal(c, input);
  };

  { // Empty container.
    // empty_container.assign_range(empty_range)
    assert(test(initial_empty, input_empty));
    // empty_container.assign_range(one_element_range)
    assert(test(initial_empty, input_one_element));
    // empty_container.assign_range(mid_range)
    assert(test(initial_empty, input_mid_range));
    // empty_container.assign_range(long_range)
    assert(test(initial_empty, input_long_range));
  }

  { // One-element container.
    // one_element_container.assign_range(empty_range)
    assert(test(initial_one_element, input_empty));
    // one_element_container.assign_range(one_element_range)
    assert(test(initial_one_element, input_one_element));
    // one_element_container.assign_range(mid_range)
    assert(test(initial_one_element, input_mid_range));
    // one_element_container.assign_range(long_range)
    assert(test(initial_one_element, input_long_range));
  }

  { // Full container.
    // full_container.assign_range(empty_range)
    assert(test(initial_full, input_empty));
    // full_container.assign_range(one_element_range)
    assert(test(initial_full, input_one_element));
    // full_container.assign_range(mid_range)
    assert(test(initial_full, input_mid_range));
    // full_container.assign_range(long_range)
    assert(test(initial_full, input_long_range));
  }
}

// Move-only types.

template <template <class ...> class Container>
constexpr void test_sequence_insert_range_move_only() {
  MoveOnly input[5];
  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});

  Container<MoveOnly> c;
  c.insert_range(c.end(), in);
}

template <template <class ...> class Container>
constexpr void test_sequence_prepend_range_move_only() {
  MoveOnly input[5];
  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});

  Container<MoveOnly> c;
  c.prepend_range(in);
}

template <template <class ...> class Container>
constexpr void test_sequence_append_range_move_only() {
  MoveOnly input[5];
  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});

  Container<MoveOnly> c;
  c.append_range(in);
}

template <template <class ...> class Container>
constexpr void test_sequence_assign_range_move_only() {
  MoveOnly input[5];
  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});

  Container<MoveOnly> c;
  c.assign_range(in);
}

// Exception safety.

template <template <class ...> class Container>
void test_insert_range_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  constexpr int ThrowOn = 3;
  using T = ThrowingCopy<ThrowOn>;
  test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
    Container<T> c;
    c.insert_range(c.end(), std::ranges::subrange(from, to));
  });
#endif
}

template <template <class ...> class Container, class T>
void test_insert_range_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  T in[] = {0, 1};

  try {
    ThrowingAllocator<T> alloc;

    globalMemCounter.reset();
    Container<T, ThrowingAllocator<T>> c(alloc);
    c.insert_range(c.end(), in);
    assert(false); // The function call above should throw.

  } catch (int) {
    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
  }
#endif
}

template <template <class ...> class Container>
void test_prepend_range_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  constexpr int ThrowOn = 3;
  using T = ThrowingCopy<ThrowOn>;
  test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
    Container<T> c;
    c.prepend_range(std::ranges::subrange(from, to));
  });
#endif
}

template <template <class ...> class Container, class T>
void test_prepend_range_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  T in[] = {0, 1};

  try {
    ThrowingAllocator<T> alloc;

    globalMemCounter.reset();
    Container<T, ThrowingAllocator<T>> c(alloc);
    c.prepend_range(in);
    assert(false); // The function call above should throw.

  } catch (int) {
    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
  }
#endif
}

template <template <class ...> class Container>
void test_append_range_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  constexpr int ThrowOn = 3;
  using T = ThrowingCopy<ThrowOn>;
  test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
    Container<T> c;
    c.append_range(std::ranges::subrange(from, to));
  });
#endif
}

template <template <class ...> class Container, class T>
void test_append_range_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  T in[] = {0, 1};

  try {
    ThrowingAllocator<T> alloc;

    globalMemCounter.reset();
    Container<T, ThrowingAllocator<T>> c(alloc);
    c.append_range(in);
    assert(false); // The function call above should throw.

  } catch (int) {
    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
  }
#endif
}

template <template <class ...> class Container>
void test_assign_range_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  constexpr int ThrowOn = 3;
  using T = ThrowingCopy<ThrowOn>;
  test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
    Container<T> c;
    c.assign_range(std::ranges::subrange(from, to));
  });
#endif
}

template <template <class ...> class Container, class T>
void test_assign_range_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
  T in[] = {0, 1};

  try {
    ThrowingAllocator<T> alloc;

    globalMemCounter.reset();
    Container<T, ThrowingAllocator<T>> c(alloc);
    c.assign_range(in);
    assert(false); // The function call above should throw.

  } catch (int) {
    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
  }
#endif
}

#endif // SUPPORT_INSERT_RANGE_SEQUENCE_CONTAINERS_H