From 01c96396463fdfc0316b10c79bf83bf1488459cb Mon Sep 17 00:00:00 2001 From: lyken Date: Sat, 24 Aug 2024 15:37:45 +0800 Subject: [PATCH] core/irrt: add Slice and Range Needed for implementing general ndarray indexing. Currently IRRT slice and range have nothing to do with NAC3's slice and range. The IRRT slice and range are currently there to implement ndarray specific features. However, in the future their definitions may be used to replace that of NAC3's. (NAC3's range is a [i32 x 3], IRRT's range is a proper struct. NAC3 does not have a slice struct). --- nac3core/irrt/irrt.cpp | 1 + nac3core/irrt/irrt/range.hpp | 47 +++++++++++ nac3core/irrt/irrt/slice.hpp | 150 ++++++++++++++++++++++++++++++++--- 3 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 nac3core/irrt/irrt/range.hpp diff --git a/nac3core/irrt/irrt.cpp b/nac3core/irrt/irrt.cpp index 58d18f8a..43f15e8f 100644 --- a/nac3core/irrt/irrt.cpp +++ b/nac3core/irrt/irrt.cpp @@ -3,6 +3,7 @@ #include "irrt/list.hpp" #include "irrt/math.hpp" #include "irrt/ndarray.hpp" +#include "irrt/range.hpp" #include "irrt/slice.hpp" #include "irrt/ndarray/basic.hpp" #include "irrt/ndarray/def.hpp" diff --git a/nac3core/irrt/irrt/range.hpp b/nac3core/irrt/irrt/range.hpp new file mode 100644 index 00000000..e9d4e612 --- /dev/null +++ b/nac3core/irrt/irrt/range.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "irrt/debug.hpp" +#include "irrt/int_types.hpp" + +namespace { +namespace range { +template +T len(T start, T stop, T step) { + // Reference: + // https://github.com/python/cpython/blob/9dbd12375561a393eaec4b21ee4ac568a407cdb0/Objects/rangeobject.c#L933 + if (step > 0 && start < stop) + return 1 + (stop - 1 - start) / step; + else if (step < 0 && start > stop) + return 1 + (start - 1 - stop) / (-step); + else + return 0; +} +} // namespace range + +/** + * @brief A Python range. + */ +template +struct Range { + T start; + T stop; + T step; + + /** + * @brief Calculate the `len()` of this range. + */ + template + T len() { + debug_assert(SizeT, step != 0); + return range::len(start, stop, step); + } +}; +} // namespace + +extern "C" { +using namespace range; + +SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { + return len(start, end, step); +} +} \ No newline at end of file diff --git a/nac3core/irrt/irrt/slice.hpp b/nac3core/irrt/irrt/slice.hpp index a1523ddc..4cf13e05 100644 --- a/nac3core/irrt/irrt/slice.hpp +++ b/nac3core/irrt/irrt/slice.hpp @@ -1,6 +1,145 @@ #pragma once +#include "irrt/debug.hpp" +#include "irrt/exception.hpp" #include "irrt/int_types.hpp" +#include "irrt/math_util.hpp" +#include "irrt/range.hpp" + +namespace { +namespace slice { +/** + * @brief Resolve a possibly negative index in a list of a known length. + * + * Returns -1 if the resolved index is out of the list's bounds. + */ +template +T resolve_index_in_length(T length, T index) { + T resolved = index < 0 ? length + index : index; + if (0 <= resolved && resolved < length) { + return resolved; + } else { + return -1; + } +} + +/** + * @brief Resolve a slice as a range. + * + * This is equivalent to `range(*slice(start, stop, step).indices(length))` in Python. + */ +template +void indices(bool start_defined, + T start, + bool stop_defined, + T stop, + bool step_defined, + T step, + T length, + T* range_start, + T* range_stop, + T* range_step) { + // Reference: https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + *range_step = step_defined ? step : 1; + bool step_is_negative = *range_step < 0; + + T lower, upper; + if (step_is_negative) { + lower = -1; + upper = length - 1; + } else { + lower = 0; + upper = length; + } + + if (start_defined) { + *range_start = start < 0 ? max(lower, start + length) : min(upper, start); + } else { + *range_start = step_is_negative ? upper : lower; + } + + if (stop_defined) { + *range_stop = stop < 0 ? max(lower, stop + length) : min(upper, stop); + } else { + *range_stop = step_is_negative ? lower : upper; + } +} +} // namespace slice + +/** + * @brief A Python-like slice with **unresolved** indices. + */ +template +struct Slice { + bool start_defined; + T start; + + bool stop_defined; + T stop; + + bool step_defined; + T step; + + Slice() { this->reset(); } + + void reset() { + this->start_defined = false; + this->stop_defined = false; + this->step_defined = false; + } + + void set_start(T start) { + this->start_defined = true; + this->start = start; + } + + void set_stop(T stop) { + this->stop_defined = true; + this->stop = stop; + } + + void set_step(T step) { + this->step_defined = true; + this->step = step; + } + + /** + * @brief Resolve this slice as a range. + * + * In Python, this would be `range(*slice(start, stop, step).indices(length))`. + */ + template + Range indices(T length) { + // Reference: + // https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + debug_assert(SizeT, length >= 0); + + Range result; + slice::indices(start_defined, start, stop_defined, stop, step_defined, step, length, &result.start, + &result.stop, &result.step); + return result; + } + + /** + * @brief Like `.indices()` but with assertions. + */ + template + Range indices_checked(T length) { + // TODO: Switch to `SizeT length` + + if (length < 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "length should not be negative, got {0}", length, NO_PARAM, + NO_PARAM); + } + + if (this->step_defined && this->step == 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "slice step cannot be zero", NO_PARAM, NO_PARAM, NO_PARAM); + } + + return this->indices(length); + } +}; +} // namespace extern "C" { SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { @@ -14,15 +153,4 @@ SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { } return i; } - -SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { - SliceIndex diff = end - start; - if (diff > 0 && step > 0) { - return ((diff - 1) / step) + 1; - } else if (diff < 0 && step < 0) { - return ((diff + 1) / step) + 1; - } else { - return 0; - } } -} \ No newline at end of file