#pragma once
#include <concepts>
#include <limits>
#include <utility>
#include "sleipnir/util/assert.hpp"
#include "sleipnir/util/symbol_exports.hpp"
namespace slp {
namespace slicing {
struct none_t {};
static inline constexpr none_t _;
}
class SLEIPNIR_DLLEXPORT Slice {
public:
int start = 0;
int stop = 0;
int step = 1;
constexpr Slice() = default;
constexpr Slice(slicing::none_t) : Slice(0, std::numeric_limits<int>::max(), 1) {}
constexpr Slice(int start) { this->start = start;
this->stop = (start == -1) ? std::numeric_limits<int>::max() : start + 1;
this->step = 1;
}
template <typename Start, typename Stop>
requires(std::same_as<Start, slicing::none_t> ||
std::convertible_to<Start, int>) &&
(std::same_as<Stop, slicing::none_t> ||
std::convertible_to<Stop, int>)
constexpr Slice(Start start, Stop stop)
: Slice(std::move(start), std::move(stop), 1) {}
template <typename Start, typename Stop, typename Step>
requires(std::same_as<Start, slicing::none_t> ||
std::convertible_to<Start, int>) &&
(std::same_as<Stop, slicing::none_t> ||
std::convertible_to<Stop, int>) &&
(std::same_as<Step, slicing::none_t> ||
std::convertible_to<Step, int>)
constexpr Slice(Start start, Stop stop, Step step) {
if constexpr (std::same_as<Step, slicing::none_t>) {
this->step = 1;
} else {
slp_assert(step != 0);
this->step = step;
}
if (this->step == std::numeric_limits<int>::min()) {
this->step = -std::numeric_limits<int>::max();
}
if constexpr (std::same_as<Start, slicing::none_t>) {
if (this->step < 0) {
this->start = std::numeric_limits<int>::max();
} else {
this->start = 0;
}
} else {
this->start = start;
}
if constexpr (std::same_as<Stop, slicing::none_t>) {
if (this->step < 0) {
this->stop = std::numeric_limits<int>::min();
} else {
this->stop = std::numeric_limits<int>::max();
}
} else {
this->stop = stop;
}
}
constexpr int adjust(int length) {
slp_assert(step != 0);
slp_assert(step >= -std::numeric_limits<int>::max());
if (start < 0) {
start += length;
if (start < 0) {
start = (step < 0) ? -1 : 0;
}
} else if (start >= length) {
start = (step < 0) ? length - 1 : length;
}
if (stop < 0) {
stop += length;
if (stop < 0) {
stop = (step < 0) ? -1 : 0;
}
} else if (stop >= length) {
stop = (step < 0) ? length - 1 : length;
}
if (step < 0) {
if (stop < start) {
return (start - stop - 1) / -step + 1;
} else {
return 0;
}
} else {
if (start < stop) {
return (stop - start - 1) / step + 1;
} else {
return 0;
}
}
}
};
}