flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:43:06 -05:00
commit 996151a56a
521 changed files with 46722 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include "../../testing/catch.hpp"
#include "../enumerate.hpp"
#include "../../containers/Vector.hpp"
#include "../../containers/List.hpp"
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::enumerate")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l;
l.resize(42, 1);
WHEN("iterating")
{
size_t last = 0;
for (auto [e, v]: enumerate(l))
{
last = e;
REQUIRE(v == 1);
}
auto expected = l.size() - 1;
REQUIRE(last == expected);
}
WHEN("iterating and setting")
{
size_t last = 0;
for (auto [e, v]: enumerate(l))
{
v = (int)e;
}
for (auto [e, v]: enumerate(l))
{
last = e;
REQUIRE(v == (int)e);
}
auto expected = l.size() - 1;
REQUIRE(last == expected);
}
WHEN("iterating const")
{
const auto &lc = l;
size_t last = 0;
for (auto [e, v]: enumerate(lc))
{
last = e;
REQUIRE(v == 1);
}
auto expected = l.size() - 1;
REQUIRE(last == expected);
}
WHEN("iterating value")
{
const auto &lc = l;
size_t last = 0;
for (auto [e, v]: enumerate_value(lc))
{
last = e;
REQUIRE(v == 1);
}
auto expected = l.size() - 1;
REQUIRE(last == expected);
}
}
GIVEN("a list")
{
List<int> l;
l.resize(42, 1);
WHEN("iterating to X")
{
size_t last = 0;
for (auto [e, v]: enumerate(l))
{
last = e;
REQUIRE(v == 1);
}
auto expected = l.size() - 1;
REQUIRE(last == expected);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,31 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/no_first.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
namespace tjp::core {
template<typename T>
struct fail_on_copy_vector : Vector<T> {
fail_on_copy_vector(const fail_on_copy_vector &) {
REQUIRE(false);
}
fail_on_copy_vector(fail_on_copy_vector &&) {
REQUIRE(false);
}
fail_on_copy_vector(std::initializer_list<T> &&v) :
Vector<T>(v)
{}
fail_on_copy_vector() {}
} ;
} // namespace

View File

@@ -0,0 +1,42 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/is_first.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::is_first")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l;
l.resize(42, 1);
int expected = 10;
l[0] = expected;
WHEN("iterating to X")
{
int value = 0;
is_first first;
for (auto &v: l)
{
if (first)
value = v;
}
REQUIRE(value == expected);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,76 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/no_first.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::no_first")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l;
l.resize(42, 1);
l.front() = 10;
l.back() = 100;
WHEN("iterating to X")
{
size_t sum = 0;
for (auto v: no_first(l))
sum += v;
auto expected = 100 + 40;
REQUIRE(sum == expected);
}
}
GIVEN("a list")
{
List<int> l;
l.resize(42, 1);
l.front() = 10;
l.back() = 100;
WHEN("iterating to X")
{
size_t sum = 0;
for (auto v: no_first(l))
sum += v;
auto expected = 100 + 40;
REQUIRE(sum == expected);
}
}
GIVEN("an empty list")
{
List<int> l;
WHEN("iterating")
{
size_t sum = 0;
size_t count = 0;
for (auto v: no_first(l))
{
sum += v;
++count;
}
REQUIRE(sum == 0);
REQUIRE(count == 0);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,76 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/no_last.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::no_last")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l;
l.resize(42, 1);
l.front() = 10;
l.back() = 100;
WHEN("iterating to X")
{
size_t sum = 0;
for (auto v: no_last(l))
sum += v;
auto expected = 10 + 40;
REQUIRE(sum == expected);
}
}
GIVEN("a list")
{
List<int> l;
l.resize(42, 1);
l.front() = 10;
l.back() = 100;
WHEN("iterating to X")
{
size_t sum = 0;
for (auto v: no_last(l))
sum += v;
auto expected = 10 + 40;
REQUIRE(sum == expected);
}
}
GIVEN("an empty list")
{
List<int> l;
WHEN("iterating")
{
size_t sum = 0;
size_t count = 0;
for (auto v: no_last(l))
{
sum += v;
++count;
}
REQUIRE(sum == 0);
REQUIRE(count == 0);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,61 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/range.hpp>
#include <tjp/core/algorithm/unused.hpp>
#include <list>
namespace tjp::core {
namespace {
SCENARIO("core::iterators::range")
{
GIVEN("a range")
{
WHEN("iterating to X")
{
auto X = 100;
auto j = 0;
for (auto i: range(X))
{
unused(i);
j++;
}
REQUIRE(j == X);
}
WHEN("iterating X to Y")
{
auto X = 50, Y = 100;
auto j = 0;
for (auto i: range(X, Y))
{
unused(i);
j++;
}
REQUIRE(j == (Y-X));
}
WHEN("iterating Y to X")
{
auto X = 50, Y = 100;
auto j = 0;
for (auto i: range(Y, X, -1))
{
unused(i);
j++;
}
REQUIRE(j == (Y-X));
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,57 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/range_with.hpp>
#include <tjp/core/iterators/enumerate.hpp>
#include <tjp/core/algorithm/unused.hpp>
#include <tjp/core/containers/Vector.hpp>
#include "fail_on_copy_vector.hpp"
#include <list>
namespace tjp::core {
namespace {
SCENARIO("core::iterators::range_with")
{
fail_on_copy_vector<int> l;
l.resize(42);
for (auto [i, v]: enumerate(l))
v = (int)i;
GIVEN("a range")
{
WHEN("iterating first 5")
{
auto X = 5;
auto j = 0;
for (auto i: range_with(l, X))
{
unused(i);
j += i;
}
REQUIRE(j == (0+1+2+3+4));
}
WHEN("iterating next 5")
{
auto X = 5;
auto j = 0;
for (auto i: range_with(l, X, X))
{
unused(i);
j += i;
}
REQUIRE(j == (5+6+7+8+9));
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,43 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/is_first.hpp>
#include <tjp/core/iterators/reverse.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::reverse")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l;
l.resize(42, 1);
int expected = 10;
l.back() = expected;
WHEN("iterating to X")
{
int value = 0;
is_first first;
for (auto &v: reverse(l))
{
if (first)
value = v;
}
REQUIRE(value == expected);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,85 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/safe_next.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
namespace tjp::core {
namespace {
SCENARIO("core::iterators::safe_next")
{
GIVEN("a list")
{
List<int> l;
l.resize(42, 1);
WHEN("iterating to X")
{
auto v = 0;
for (auto i: safe_next(l))
v += *i;
REQUIRE(v == 42);
}
WHEN("iterating to X")
{
auto v = 0;
auto e = -1;
for (auto i: safe_next(l))
{
++e;
v += *i;
if (e % 2 == 0)
{
l.erase(i);
}
}
REQUIRE(v == 42);
}
WHEN("iterating to X with vaue")
{
auto v = 0;
for (auto x: safe_next_value(l))
v += x;
REQUIRE(v == 42);
}
WHEN("iterating to X with value")
{
auto v = 0;
auto e = -1;
auto i = l.begin();
for (auto &x: safe_next_value(l))
{
auto n = i;
n++;
++e;
v += x;
if (e % 2 == 0)
{
l.erase(i);
}
i = n;
}
REQUIRE(v == 42);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,75 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include "../../testing/catch.hpp"
#include "../transform_with.hpp"
#include "../enumerate.hpp"
#include "../../containers/Vector.hpp"
#include "../../containers/List.hpp"
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::transform_with")
{
GIVEN("a vector")
{
fail_on_copy_vector<int> l = { 1, 3, 5, 7, 9 };
auto make_even = [](auto &v) { return v + 1; };
WHEN("transform_with")
{
for (auto v: transform_with(l, make_even))
{
auto is_even = (v % 2) == 0;
REQUIRE(is_even);
}
}
WHEN("transform_with with enumerate")
{
for (auto [e, v]: enumerate_value(transform_with(l, make_even)))
{
auto is_even = (v % 2) == 0;
REQUIRE(is_even);
}
}
WHEN("transform_with reference")
{
auto make_even = [](auto &v) -> int & { v+=1; return v; };
for (auto v: transform_with(l, make_even))
{
auto is_even = (v % 2) == 0;
REQUIRE(is_even);
}
}
WHEN("transform_with reference enumerate")
{
auto make_even = [](auto &v) -> int & { ++v; return v; };
for (auto [e, v]: enumerate(transform_with(l, make_even)))
{
auto is_even = (v % 2) == 0;
REQUIRE(is_even);
v = e;
}
for (auto [e, v]: enumerate_value(transform_with(l, make_even)))
{
REQUIRE(v == e+1);
}
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,96 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/iterators/zip.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/List.hpp>
#include "fail_on_copy_vector.hpp"
namespace tjp::core {
namespace {
SCENARIO("core::iterators::zip")
{
constexpr size_t size = 42;
GIVEN("two vector")
{
fail_on_copy_vector<int> l;
fail_on_copy_vector<char> r;
l.resize(size);
r.resize(size);
for (auto [lv, rv]: zip(l, r))
{
lv = 1;
rv = 1;
}
WHEN("iterating to X")
{
size_t sum = 0;
for (auto [lv, rv]: zip(l, r))
sum += lv + rv;
auto expected = size * 2;
REQUIRE(sum == expected);
}
}
GIVEN("three vector")
{
fail_on_copy_vector<int> l;
fail_on_copy_vector<char> r;
fail_on_copy_vector<char> u;
l.resize(size);
r.resize(size);
u.resize(size);
for (auto [lv, rv, uv]: zip(l, r, u))
{
lv = 1;
rv = 1;
uv = 1;
}
WHEN("iterating to X")
{
size_t sum = 0;
for (auto [lv, rv, uv]: zip(l, r, u))
sum += lv + rv + uv;
auto expected = size * 3;
REQUIRE(sum == expected);
}
}
GIVEN("two list")
{
List<int> l;
List<char> r;
l.resize(size);
r.resize(size);
for (auto [lv, rv]: zip(l, r))
{
lv = 1;
rv = 1;
}
WHEN("iterating to X")
{
size_t sum = 0;
for (auto [lv, rv]: zip(l, r))
sum += lv + rv;
auto expected = size * 2;
REQUIRE(sum == expected);
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,88 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <tuple>
namespace tjp::core::iterators {
template <typename T, typename I, typename R>
struct enumerate_wrapper {
T iterable;
};
template <typename I, typename R>
struct enumerate_iterator
{
size_t i;
I iter;
bool operator != (const enumerate_iterator & other) const
{
return iter != other.iter;
}
void operator ++ ()
{
++i;
++iter;
}
auto operator * () const
{
return std::tuple<size_t, R>(i, *iter);
}
};
template <typename T, typename I, typename R>
auto begin (enumerate_wrapper<T, I, R> w)
{
return enumerate_iterator<I, R> { 0, begin(w.iterable) };
}
template <typename T, typename I, typename R>
auto end (enumerate_wrapper<T, I, R> w)
{
return enumerate_iterator<I, R> { 0, end(w.iterable) };
}
template <typename T,
typename I = decltype(begin(std::declval<T>())),
typename = decltype(end(std::declval<T>()))>
constexpr auto enumerate_ref(T && iterable)
{
using V = decltype(*std::declval<I>());
using R = V &;
return enumerate_wrapper<T, I, R> { std::forward<T>(iterable) };
}
template <typename T,
typename I = decltype(begin(std::declval<T>())),
typename = decltype(end(std::declval<T>()))>
constexpr auto enumerate_value(T && iterable)
{
using V = decltype(*std::declval<I>());
return enumerate_wrapper<T, I, V> { std::forward<T>(iterable) };
}
// TODO: the default c++ iteration is by value but here we have by ref, should it be by value?
template<typename T>
constexpr auto enumerate(T && iterable)
{
return enumerate_ref(std::forward<T>(iterable));
}
} // namespace
namespace tjp::core {
using iterators::enumerate;
using iterators::enumerate_ref;
using iterators::enumerate_value;
} // namespace

39
tjp/core/iterators/is_first.hpp Executable file
View File

@@ -0,0 +1,39 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::iterators {
struct is_first
{
bool v = true;
bool check ()
{
auto r = v;
v = false;
return r;
}
operator bool()
{
return check();
}
bool operator()()
{
return check();
}
} ;
} // namespace
namespace tjp::core {
using iterators::is_first;
} // namespace

View File

@@ -0,0 +1,14 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp {
namespace core {
#define macro_min(a,b) ((a<b)?a:b)
#define macro_max(a,b) ((a>b)?a:b)
} // namespace utilities
} // namespace

42
tjp/core/iterators/no_first.hpp Executable file
View File

@@ -0,0 +1,42 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::iterators {
template <typename T>
struct no_first_wrapper
{
T iterable;
};
template <typename T>
auto begin (no_first_wrapper<T> w)
{
auto b = std::begin(w.iterable);
auto e = std::end(w.iterable);
return (b == e) ? e : std::next(b);
}
template <typename T>
auto end (no_first_wrapper<T> w)
{
return end(w.iterable);
}
template <typename T>
no_first_wrapper<T> no_first (T&& iterable)
{
return { std::forward<T>(iterable) };
}
} // namespace
namespace tjp::core {
using iterators::no_first;
} // namespace

42
tjp/core/iterators/no_last.hpp Executable file
View File

@@ -0,0 +1,42 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::iterators {
template <typename T>
struct no_last_wrapper
{
T iterable;
};
template <typename T>
auto begin (no_last_wrapper<T> w)
{
return begin(w.iterable);
}
template <typename T>
auto end (no_last_wrapper<T> w)
{
auto b = std::begin(w.iterable);
auto e = std::end(w.iterable);
return (b == e) ? e : std::prev(e);
}
template <typename T>
no_last_wrapper<T> no_last (T&& iterable)
{
return { std::forward<T>(iterable) };
}
} // namespace
namespace tjp::core {
using iterators::no_last;
} // namespace

85
tjp/core/iterators/range.hpp Executable file
View File

@@ -0,0 +1,85 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <stddef.h>
#include "macro_min_max.h"
namespace tjp::core::iterators {
template<typename T=size_t>
class range {
public:
class iterator {
friend class range;
public:
T operator *() const { return i_; }
const iterator &operator ++() {
int inc = inc_;
while(inc > 0) {
++i_;
--inc;
}
while(inc < 0) {
--i_;
++inc;
}
return *this;
}
iterator operator ++(int)
{
iterator copy(*this);
++(*this);
return copy;
}
bool operator ==(const iterator &other) const { return i_ == other.i_; }
bool operator !=(const iterator &other) const { return i_ != other.i_; }
protected:
iterator(T start, int inc) : i_ (start), inc_(inc) { }
iterator(T start) : i_ (start), inc_(1) { }
private:
int inc_;
T i_;
};
iterator begin() const { return begin_; }
iterator end() const { return end_; }
range(T end) :
begin_(macro_min(T(0), end)),
end_(end)
{}
range(T begin, T end) :
begin_(macro_min(begin, end)),
end_(end)
{}
range(T begin, T end, int inc) :
begin_(inc > 0 ? macro_min(begin,end) : macro_max(begin,end), inc),
end_(end)
{}
private:
iterator begin_;
iterator end_;
};
} // namespace
namespace tjp::core {
using iterators::range;
} // namespace

View File

@@ -0,0 +1,70 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::iterators {
template<typename T>
struct range_over
{
T begin_;
T end_;
T begin() { return begin_; }
T end() { return end_; }
} ;
template<typename T>
auto range_with(T begin, T end)
{
return range_over<T> { begin, end };
}
template<typename T>
auto range_with(T &t)
{
return range_over { t.begin(), t.end() };
}
template<typename T>
auto range_with(const T &t)
{
return range_over { t.begin(), t.end() };
}
template<typename T>
auto range_with(T &t, size_t num)
{
return range_over<typename T::iterator> { t.begin(), t.begin() + num };
}
template<typename T>
auto range_with(const T &t, size_t num)
{
return range_over<typename T::const_iterator> { t.begin(), t.begin() + num };
}
template<typename T>
auto range_with(T &t, size_t from, size_t num)
{
return range_over<typename T::iterator> { t.begin() + from, t.begin() + from + num };
}
template<typename T>
auto range_with(const T &t, size_t from, size_t num)
{
return range_over<typename T::const_iterator> { t.begin() + from, t.begin() + from + num };
}
} // namespace
namespace tjp::core {
using iterators::range_with;
} // namespace

30
tjp/core/iterators/reverse.hpp Executable file
View File

@@ -0,0 +1,30 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <iterator>
namespace tjp::core::iterators {
template <typename T>
struct reversion_wrapper { T iterable; };
template <typename T>
auto begin (reversion_wrapper<T> w) { return rbegin(w.iterable); }
template <typename T>
auto end (reversion_wrapper<T> w) { return rend(w.iterable); }
template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { std::forward<T>(iterable) }; }
} // namespace
namespace tjp::core {
using iterators::reverse;
} // namespace

View File

@@ -0,0 +1,89 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <algorithm>
#include <tuple>
template <typename T,
typename TI = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto safe_next(T && iterable)
{
struct iterator
{
TI iter;
TI end;
TI next;
iterator(TI iter_, TI end_) :
iter(iter_),
next(iter_),
end(end_)
{
if (next != end)
++next;
}
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ ()
{
iter = next;
if (next != end)
++next;
}
const TI &operator * () const { return iter; }
TI &operator * () { return iter; }
};
struct iterable_wrapper
{
T &iterable;
auto begin() { return iterator(std::begin(iterable), std::end(iterable)); }
auto end() { return iterator(std::end(iterable), std::end(iterable)); }
};
return iterable_wrapper{ std::forward<T>(iterable) };
}
template <typename T,
typename TI = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto safe_next_value(T && iterable)
{
struct iterator
{
TI iter;
TI end;
TI next;
iterator(TI iter_, TI end_) :
iter(iter_),
next(iter_),
end(end_)
{
if (next != end)
++next;
}
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ ()
{
iter = next;
if (next != end)
++next;
}
auto &operator * () const { return *iter; }
auto &operator * () { return *iter; }
};
struct iterable_wrapper
{
T &iterable;
auto begin() { return iterator(std::begin(iterable), std::end(iterable)); }
auto end() { return iterator(std::end(iterable), std::end(iterable)); }
};
return iterable_wrapper{ std::forward<T>(iterable) };
}

View File

@@ -0,0 +1,87 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <tuple>
template <typename T,
typename TI = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto safe_next_with(T && iterable, TI iter)
{
struct iterator
{
TI iter;
TI end;
TI next;
iterator(TI iter_, TI end_) :
iter(iter_),
next(iter_),
end(end_)
{
if (next != end)
++next;
}
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ ()
{
iter = next;
if (next != end)
++next;
}
const TI &operator * () const { return iter; }
TI &operator * () { return iter; }
bool operator !=(const TI &rhs)
{
return iter != rhs;
}
};
return iterator{ iter, iterable.end() };
}
template <typename T,
typename TI = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto safe_next_with_value(T && iterable, TI iter)
{
struct iterator
{
TI iter;
TI end;
TI next;
iterator(TI iter_, TI end_) :
iter(iter_),
next(iter_),
end(end_)
{
if (next != end)
++next;
}
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ ()
{
iter = next;
if (next != end)
++next;
}
auto &operator * () const { return *iter; }
auto &operator * () { return *iter; }
bool operator !=(const TI &rhs)
{
return iter != rhs;
}
};
return iterator{ iter, iterable.end() };
}

View File

@@ -0,0 +1,67 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <vector>
namespace tjp {
namespace core {
namespace iterators {
template <typename T,
typename F,
typename I = decltype(begin(std::declval<T>()))
>
struct transform_with_iterator : I
{
typedef I Super;
F f;
using R = decltype(std::declval<F>()(*std::declval<I>()));
transform_with_iterator(Super i, F &f_) :
Super(i),
f(f_)
{}
R operator *() const
{
return f(Super::operator*());
}
} ;
template <typename T, typename F>
struct transform_with_wrapper {
T iterable;
F f;
};
template <typename T, typename F>
auto begin (transform_with_wrapper<T, F> w)
{
return transform_with_iterator<T, F>(begin(w.iterable), w.f);
}
template <typename T, typename F>
auto end (transform_with_wrapper<T, F> w)
{
return transform_with_iterator<T, F>(end(w.iterable), w.f);
}
template <typename T, typename F>
transform_with_wrapper<T, F> transform_with (T&& iterable, F &&f)
{
return { std::forward<T>(iterable), std::forward<F>(f) };
}
} // namespace
using iterators::transform_with;
using iterators::transform_with_wrapper;
using iterators::transform_with_iterator;
} // namespace
} // namespace

151
tjp/core/iterators/zip.hpp Executable file
View File

@@ -0,0 +1,151 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::iterators {
namespace zip_detail {
template <typename Iter>
using select_access_type_for = std::conditional_t<
std::is_same_v<Iter, std::vector<bool>::iterator> ||
std::is_same_v<Iter, std::vector<bool>::const_iterator>,
typename Iter::value_type,
typename Iter::reference
>;
template <typename ... Args, std::size_t ... Index>
auto any_match_impl(std::tuple<Args...> const & lhs,
std::tuple<Args...> const & rhs,
std::index_sequence<Index...>) -> bool
{
auto result = false;
result = (... | (std::get<Index>(lhs) == std::get<Index>(rhs)));
return result;
}
template <typename ... Args>
auto any_match(std::tuple<Args...> const & lhs, std::tuple<Args...> const & rhs)
-> bool
{
return any_match_impl(lhs, rhs, std::index_sequence_for<Args...>{});
}
template <typename ... Iters>
class zip_iterator
{
public:
using value_type = std::tuple<
select_access_type_for<Iters>...
>;
zip_iterator() = delete;
zip_iterator(Iters && ... iters)
: m_iters {std::forward<Iters>(iters)...}
{
}
auto operator++() -> zip_iterator&
{
std::apply(
[](auto && ... args) {
(
(++args),
...
);
},
m_iters
);
return *this;
}
auto operator++(int) -> zip_iterator
{
auto tmp = *this;
++*this;
return tmp;
}
auto operator!=(zip_iterator const & other)
{
return !(*this == other);
}
auto operator==(zip_iterator const & other)
{
return any_match(m_iters, other.m_iters);
}
auto operator*() -> value_type
{
return std::apply([](auto && ... args){
return value_type(*args...);
}, m_iters);
}
private:
std::tuple<Iters...> m_iters;
};
/* std::decay needed because T is a reference, and is not a complete type */
template <typename T>
using select_iterator_for = std::conditional_t<
std::is_const_v<std::remove_reference_t<T>>,
typename std::decay_t<T>::const_iterator,
typename std::decay_t<T>::iterator>;
template <typename ... T>
class zipper
{
public:
using zip_type = zip_iterator<select_iterator_for<T> ...>;
template <typename ... Args>
zipper(Args && ... args)
: m_args{std::forward<Args>(args)...}
{
}
auto begin() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::begin(args)...);
}, m_args);
}
auto end() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::end(args)...);
}, m_args);
}
private:
std::tuple<T ...> m_args;
};
template <typename ... T>
auto zip(T && ... t)
{
return zipper<T ...>{std::forward<T>(t)...};
}
} // namespace zip_detail
} // namespace
namespace tjp::core {
using iterators::zip_detail::zip;
} // namespace