Three ways of indexing into a tensor/array look similar (all three select a subset of elements), but they have a genuinely important difference underneath: some return a view into the original memory, others return an independent copy. Code that treats every indexed subset the same way will work fine for one kind and silently misbehave for another the moment it mutates the result.
Implement basic_slice, boolean_mask, fancy_index, and is_a_view_of — a way to actually verify, rather than assume, whether a given result shares memory with its source.
basic_slice(a, start, stop) returns a[start:stop], sharing memory with a.boolean_mask(a, mask) and fancy_index(a, indices) both return independent copies, not sharing memory with a.is_a_view_of(child, parent) returns a plain bool: True iff child and parent share underlying memory.Open one at a time. Each gives away a little more than the last.
None of the three indexing functions need any logic beyond the indexing expression itself — the view-vs-copy behavior comes from NumPy, based purely on which kind of indexing syntax is used.
is_a_view_of doesn't need to inspect strides or offsets by hand — NumPy has a function whose entire job is answering exactly this question.
Click "Run Tests" to test your implementation