// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// #region type definition and intrinsics

///|
/// An `ArrayView` represents a view into a section of an array without copying the data.
/// It stores its own start offset and length when it is created, so iteration
/// over a view keeps using those bounds even if the underlying array is later
/// structurally modified.
///
/// # Example
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4] // Creates a view of elements at indices 1,2,3
///   @test.assert_eq(view[0], 2)
///   @test.assert_eq(view.length(), 3)
/// }
/// ```
#builtin.valtype
type ArrayView[T]

///|
fn[T] ArrayView::buf(self : ArrayView[T]) -> UninitializedArray[T] = "%arrayview.buf"

///|
fn[T] ArrayView::start(self : ArrayView[T]) -> Int = "%arrayview.start"

///|
fn[T] ArrayView::len(self : ArrayView[T]) -> Int = "%arrayview.len"

///|
fn[T] ArrayView::make(
  buf : UninitializedArray[T],
  start : Int,
  len : Int,
) -> ArrayView[T] = "%arrayview.make"

// #endregion

// #region methods

///|
/// Returns the length (number of elements) of an array view.
///
/// Parameters:
///
/// * `array_view` : The array view whose length is to be determined.
///
/// Returns an integer representing the number of elements in the array view.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[2:4]
///   inspect(view.length(), content="2")
/// }
/// ```
#intrinsic("%arrayview.length")
pub fn[T] ArrayView::length(self : ArrayView[T]) -> Int {
  self.len()
}

///|
/// Returns whether the array view is empty.
///
/// Example:
///
/// ```mbt check
/// test {
///   let view = [1, 2, 3][:]
///   inspect(view.is_empty(), content="false")
///   let empty = [1, 2][0:0]
///   inspect(empty.is_empty(), content="true")
/// }
/// ```
pub fn[T] ArrayView::is_empty(self : ArrayView[T]) -> Bool {
  self.length() == 0
}

///|
/// Return the starting index of this view in the underlying array.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [10, 20, 30]
///   let v = arr[1:]
///   inspect(v.start_offset(), content="1")
/// }
/// ```
pub fn[T] ArrayView::start_offset(self : Self[T]) -> Int {
  self.start()
}

///|
/// Retrieves an element at the specified index from the array view.
///
/// Parameters:
///
/// * `self` : The array view to access.
/// * `index` : The position in the array view from which to retrieve the
/// element.
///
/// Returns the element at the specified index.
///
/// Throws a runtime error if the index is out of bounds (less than 0 or greater
/// than or equal to the length of the array view).
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[2:4]
///   inspect(view[0], content="3")
///   inspect(view[1], content="4")
/// }
/// ```
#intrinsic("%arrayview.get")
#alias("_[_]")
pub fn[T] ArrayView::at(self : ArrayView[T], index : Int) -> T {
  guard index >= 0 && index < self.len() else {
    index_out_of_bounds(self.len(), index)
  }
  self.buf().unsafe_get(self.start() + index)
}

///|
/// Retrieves an element from the array view at the specified index.
///
/// Parameters:
///
/// * `self` : The array view to retrieve the element from.
/// * `index` : The position in the array view from which to retrieve the
/// element.
///
/// Returns `Some(element)` if the index is within bounds, or `None` if the index
/// is out of bounds.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4]
///   debug_inspect(view.get(0), content="Some(2)")
///   debug_inspect(view.get(1), content="Some(3)")
///   debug_inspect(view.get(2), content="Some(4)")
///   debug_inspect(view.get(5), content="None")
/// }
/// ```
pub fn[T] ArrayView::get(self : ArrayView[T], index : Int) -> T? {
  let len = self.length()
  guard index >= 0 && index < len else { None }
  Some(self.buf().unsafe_get(self.start() + index))
}

///|
/// Returns the last element of the array view, if any.
///
/// Example:
///
/// ```mbt check
/// test {
///   let view = [1, 2, 3][:]
///   debug_inspect(view.last(), content="Some(3)")
///   let empty = [1, 2][0:0]
///   debug_inspect(empty.last(), content="None")
/// }
/// ```
pub fn[T] ArrayView::last(self : ArrayView[T]) -> T? {
  let len = self.length()
  if len == 0 {
    None
  } else {
    Some(self.unsafe_get(len - 1))
  }
}

///|
/// Retrieves an element from the array view at the specified index without
/// performing bounds checking.
///
/// Parameters:
///
/// * `array_view` : The array view to retrieve the element from.
/// * `index` : The position in the array view from which to retrieve the
/// element.
///
/// Returns the element at the specified index in the array view.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4]
///   inspect(view.unsafe_get(1), content="3")
/// }
/// ```
#intrinsic("%arrayview.unsafe_get")
#internal(unsafe, "Panic if index is out of bounds")
#doc(hidden)
pub fn[T] ArrayView::unsafe_get(self : ArrayView[T], index : Int) -> T {
  self.buf()[self.start() + index]
}

///|
/// Creates a view of a portion of the array. The view provides read-write access
/// to the underlying array without copying the elements.
///
/// Parameters:
///
/// * `array` : The array to create a view from.
/// * `start` : The starting index of the view (inclusive). Defaults to 0.
/// * `end` : The ending index of the view (exclusive). If not provided, defaults
/// to the length of the array.
///
/// Returns an `ArrayView` that provides a window into the specified portion of
/// the array.
///
/// Throws a panic if the indices are invalid (i.e., `start` is negative, `end`
/// is greater than the array length, or `start` is greater than `end`).
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4] // Create a view of elements at indices 1, 2, and 3
///   inspect(view[0], content="2") // First element of view is arr[1]
///   inspect(view.length(), content="3") // View contains 3 elements
/// }
/// ```
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn[T] Array::view(
  self : Array[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T] {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else {
    abort("View index out of bounds")
  }
  ArrayView::make(self.buffer(), start, end - start)
}

///|
/// Creates a view of a portion of the array, returning `None` when indices are
/// invalid.
///
/// Parameters:
///
/// * `array` : The array to create a view from.
/// * `start` : The starting index of the view (inclusive). Defaults to 0.
/// * `end` : The ending index of the view (exclusive). If not provided, defaults
/// to the length of the array.
///
/// Returns `Some(ArrayView)` that provides a window into the specified portion
/// of the array, or `None` when the indices are invalid.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let start = 1
///   let end = 4
///   debug_inspect(
///     arr.get_view(start~, end~),
///     content=(
///       #|Some()
///     ),
///   )
///   let start = 3
///   let end = 10
///   debug_inspect(arr.get_view(start~, end~), content="None")
/// }
/// ```
pub fn[T] Array::get_view(
  self : Array[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T]? {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else { None }
  Some(ArrayView::make(self.buffer(), start, end - start))
}

///|
/// Creates a new view into a portion of the array view.
///
/// Parameters:
///
/// * `self` : The array view to create a new view from.
/// * `start` : The starting index in the current view (inclusive). Defaults to
/// 0.
/// * `end` : The ending index in the current view (exclusive). Defaults to the
/// length of the current view.
///
/// Returns a new `ArrayView` that provides a window into the specified portion
/// of the original array view. The indices are relative to the start of the
/// current view.
///
/// Throws a panic if:
///
/// * `start` is negative
/// * `end` is greater than the length of the current view
/// * `start` is greater than `end`
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4] // view = [2, 3, 4]
///   let subview = view[1:2] // subview = [3]
///   inspect(subview[0], content="3")
/// }
/// ```
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn[T] ArrayView::view(
  self : ArrayView[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T] {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else {
    abort("View index out of bounds")
  }
  ArrayView::make(self.buf(), self.start() + start, end - start)
}

///|
/// Creates a new view into a portion of the array view, returning `None` when
/// indices are invalid.
///
/// Parameters:
///
/// * `self` : The array view to create a new view from.
/// * `start` : The starting index in the current view (inclusive). Defaults to
/// 0.
/// * `end` : The ending index in the current view (exclusive). Defaults to the
/// length of the current view.
///
/// Returns `Some(ArrayView)` that provides a window into the specified portion
/// of the original array view, or `None` when the indices are invalid.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5]
///   let view = arr[1:4] // view = [2, 3, 4]
///   let start = 1
///   let end = 2
///   debug_inspect(
///     view.get_view(start~, end~),
///     content=(
///       #|Some()
///     ),
///   )
///   let start = 4
///   let end = 5
///   debug_inspect(view.get_view(start~, end~), content="None")
/// }
/// ```
pub fn[T] ArrayView::get_view(
  self : ArrayView[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T]? {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else { None }
  Some(ArrayView::make(self.buf(), self.start() + start, end - start))
}

///|
fn[T] unsafe_cast_fixedarray_to_uninitializedarray(
  arr : FixedArray[T],
) -> UninitializedArray[T] = "%identity"

///|
/// Creates a new `ArrayView` from a `FixedArray`.
///
/// Parameters:
///
/// * `self` : The fixed array to create a new view from.
/// * `start` : The starting index in the array (inclusive). Defaults to 0.
/// * `end` : The ending index in the array (exclusive). Defaults to the
/// length of the array.
///
/// Returns a new `ArrayView` that provides a window into the specified portion
/// of the original fixed array.
///
/// Throws a panic if:
///
/// * `start` is negative
/// * `end` is greater than the length of the array
/// * `start` is greater than `end`
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr : FixedArray[Int] = [1, 2, 3, 4, 5]
///   let view = arr[1:4] // view = [2, 3, 4]
///   inspect(view[0], content="2")
/// }
/// ```
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn[T] FixedArray::view(
  self : FixedArray[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T] {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else {
    abort("View index out of bounds")
  }
  ArrayView::make(
    unsafe_cast_fixedarray_to_uninitializedarray(self),
    start,
    end - start,
  )
}

///|
/// Creates a new `ArrayView` from a `FixedArray`, returning `None` when indices
/// are invalid.
///
/// Parameters:
///
/// * `self` : The fixed array to create a new view from.
/// * `start` : The starting index in the array (inclusive). Defaults to 0.
/// * `end` : The ending index in the array (exclusive). Defaults to the
/// length of the array.
///
/// Returns `Some(ArrayView)` that provides a window into the specified portion
/// of the original fixed array, or `None` when the indices are invalid.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr : FixedArray[Int] = [1, 2, 3, 4, 5]
///   let start = 1
///   let end = 4
///   debug_inspect(
///     arr.get_view(start~, end~),
///     content=(
///       #|Some()
///     ),
///   )
///   let start = 2
///   let end = 10
///   debug_inspect(arr.get_view(start~, end~), content="None")
/// }
/// ```
pub fn[T] FixedArray::get_view(
  self : FixedArray[T],
  start? : Int = 0,
  end? : Int,
) -> ArrayView[T]? {
  let len = self.length()
  let end = match end {
    Some(end) | (None with end = len) => end
  }
  guard start >= 0 && start <= end && end <= len else { None }
  Some(
    ArrayView::make(
      unsafe_cast_fixedarray_to_uninitializedarray(self),
      start,
      end - start,
    ),
  )
}

///|
/// Return an iterator over suffix views of this array view.
///
/// Set `include_empty=true` to include the empty suffix at the end.
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 2][:]
///   debug_inspect(
///     v.suffixes().collect(),
///     content=(
///       #|[, ]
///     ),
///   )
///   debug_inspect(
///     v.suffixes(include_empty=true).collect(),
///     content=(
///       #|[, , ]
///     ),
///   )
/// }
/// ```
pub fn[T] ArrayView::suffixes(
  self : Self[T],
  include_empty? : Bool = false,
) -> Iter[ArrayView[T]] {
  let len = self.length()
  let mut i = 0
  Iter::new(
    fn() -> ArrayView[T]? {
      if i < len {
        let suffix = self[i:]
        i += 1
        Some(suffix)
      } else if i == len {
        i += 1
        if include_empty {
          Some(self[len:])
        } else {
          None
        }
      } else {
        None
      }
    },
    size_hint=if include_empty { len + 1 } else { len },
  )
}

///|
/// Split the array view into contiguous sub-views of length `size`. The last
/// sub-view may be shorter if the view length is not a multiple of `size`. The
/// returned sub-views share the original backing array — no elements are
/// copied.
///
/// Panics if `size <= 0`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 2, 3, 4, 5, 6, 7][:]
///   debug_inspect(
///     v.chunks(3),
///     content=(
///       #|[, , ]
///     ),
///   )
/// }
/// ```
pub fn[T] ArrayView::chunks(
  self : ArrayView[T],
  size : Int,
) -> Array[ArrayView[T]] {
  guard! size > 0
  let len = self.length()
  if len == 0 {
    return []
  }
  let num_chunks = (len + size - 1) / size
  Array::makei(num_chunks, i => {
    let start = i * size
    let end = Int::min(start + size, len)
    self[start:end]
  })
}

///|
/// Groups consecutive elements of the view into chunks where adjacent
/// elements satisfy the given predicate. Each returned sub-view shares the
/// original backing array.
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 1, 2, 2, 2, 3, 1][:]
///   debug_inspect(
///     v.chunk_by((a, b) => a == b),
///     content=(
///       #|[
///       #|  ,
///       #|  ,
///       #|  ,
///       #|  ,
///       #|]
///     ),
///   )
/// }
/// ```
#locals(pred)
pub fn[T] ArrayView::chunk_by(
  self : ArrayView[T],
  pred : (T, T) -> Bool raise?,
) -> Array[ArrayView[T]] raise? {
  let chunks = []
  if self.is_empty() {
    return chunks
  }
  let start = for i in 1..,
///       #|  ,
///       #|  ,
///       #|]
///     ),
///   )
/// }
/// ```
pub fn[T] ArrayView::windows(
  self : ArrayView[T],
  size : Int,
) -> Array[ArrayView[T]] {
  guard! size > 0
  let len = self.length() - size + 1
  if len < 1 {
    return []
  }
  Array::makei(len, i => self[i:i + size])
}

///|
/// Returns an iterator that yields each element of the array view in sequence
/// from start to end.
///
/// Parameters:
///
/// * `array_view` : The array view to iterate over.
///
/// Returns an iterator that yields elements of type `A` from the array view.
/// The iterator uses the view's stored length, so structural mutations to the
/// underlying array after the view is created do not change how many steps the
/// iterator attempts to take.
///
/// Structural shrinking of the underlying array is unsupported and may cause
/// later iterator steps to fail. The same caveat applies to `rev_iter()` and
/// `iter2()`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3]
///   let view = arr[1:]
///   let mut sum = 0
///   view.iter().each(x => sum += x)
///   inspect(sum, content="5")
/// }
/// ```
#alias(iterator, deprecated)
pub fn[X] ArrayView::iter(self : ArrayView[X]) -> Iter[X] {
  let mut i = 0
  let len = self.length()
  Iter::new(
    fn() {
      guard i < len else { None }
      let elem = self.unsafe_get(i)
      i += 1
      Some(elem)
    },
    size_hint=len,
  )
}

///|
/// Return a reverse iterator over elements of this view.
///
/// Deprecated alias for reverse iteration; prefer `rev_iterator`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let values = [1, 2, 3][:].rev_iter().collect()
///   debug_inspect(values, content="[3, 2, 1]")
/// }
/// ```
#alias(rev_iterator, deprecated)
pub fn[X] ArrayView::rev_iter(self : ArrayView[X]) -> Iter[X] {
  let len = self.length()
  let mut i = len
  Iter::new(
    fn() {
      guard i > 0 else { None }
      i -= 1
      Some(self.unsafe_get(i))
    },
    size_hint=len,
  )
}

///|
/// Returns an iterator that yields tuples of index and value
/// indices start from 0.
/// 
/// Example:
/// ```mbt check
/// test {
///   let arr = [1, 2, 3]
///   let view = arr[1:]
///   let mut sum = 0
///   let mut sum_keys = 0
///   view
///   .iter2()
///   .each((i, x) => {
///     sum += x
///     sum_keys += i
///   })
///   inspect(sum, content="5")
///   inspect(sum_keys, content="1")
/// }
/// ```
#alias(iterator2, deprecated)
pub fn[X] ArrayView::iter2(self : ArrayView[X]) -> Iter2[Int, X] {
  let mut i = 0
  let len = self.length()
  Iter2::new(
    fn() {
      guard i < len else { None }
      let result = Some((i, self.unsafe_get(i)))
      i += 1
      result
    },
    size_hint=len,
  )
}

///|
/// Iterates over each element in the array view and applies a function to it.
///
/// Parameters:
///
/// * `self` : The array view to iterate over.
/// * `function` : A function that takes an element of type `T` and returns
/// nothing. This function will be applied to each element in the array view.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3][:]
///   let mut sum = 0
///   arr.each(x => sum += x)
///   inspect(sum, content="6")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::each(
  self : ArrayView[T],
  f : (T) -> Unit raise?,
) -> Unit raise? {
  for v in self {
    f(v)
  }
}

///|
/// Iterates over the elements of the array view with index.
///
/// # Example
/// 
/// ```mbt check
/// test {
///   let v = [3, 4, 5][:]
///   let mut sum = 0
///   v.eachi((i, x) => sum += x + i)
///   inspect(sum, content="15")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::eachi(
  self : ArrayView[T],
  f : (Int, T) -> Unit raise?,
) -> Unit raise? {
  for i, v in self {
    f(i, v)
  }
}

///|
/// Iterates over the elements of the array view in reverse order (last to
/// first).
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 2, 3, 4, 5][1:4]
///   let out = []
///   v.rev_each(x => out.push(x))
///   debug_inspect(out, content="[4, 3, 2]")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::rev_each(
  self : ArrayView[T],
  f : (T) -> Unit raise?,
) -> Unit raise? {
  let len = self.length()
  for i in 0.. out.push((i, x)))
///   debug_inspect(out, content="[(0, 30), (1, 20), (2, 10)]")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::rev_eachi(
  self : ArrayView[T],
  f : (Int, T) -> Unit raise?,
) -> Unit raise? {
  let len = self.length()
  for i in 0.. elem % 2 == 0))
///   assert_true(v[1:4].all(elem => elem % 2 == 0))
/// }
/// ```
#locals(f)
#alias(every)
pub fn[T] ArrayView::all(
  self : ArrayView[T],
  f : (T) -> Bool raise?,
) -> Bool raise? {
  for v in self {
    if !f(v) {
      return false
    }
  }
  true
}

///|
/// Check if any of the elements in the array view match the condition.
///
/// # Example
///
/// ```mbt check
/// test {
///   let v = [1, 2, 3, 4, 5][:]
///   assert_true(v.any(ele => ele < 6))
///   assert_false(v.any(ele => ele < 1))
/// }
/// ```
#locals(f)
#alias(exists)
pub fn[T] ArrayView::any(
  self : ArrayView[T],
  f : (T) -> Bool raise?,
) -> Bool raise? {
  for v in self {
    if f(v) {
      return true
    }
  }
  false
}

///|
/// Checks whether the array view contains a specific element by comparing each
/// element with the target value using the equality operator.
///
/// Parameters:
///
/// * `view` : The array view to search in.
/// * `target` : The value to search for in the array view.
///
/// Returns a boolean value indicating whether the target value exists in the
/// array view.
///
/// Example:
///
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5][:]
///   inspect(arr.contains(3), content="true")
///   inspect(arr.contains(6), content="false")
/// }
/// ```
pub fn[T : Eq] ArrayView::contains(self : ArrayView[T], value : T) -> Bool {
  for v in self {
    if v == value {
      break true
    }
  } nobreak {
    false
  }
}

///|
/// Counts how many elements in the array view are equal to `value`.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 2, 1, 3, 1][:]
///   inspect(view.count(1), content="3")
///   inspect(view.count(4), content="0")
/// }
/// ```
pub fn[T : Eq] ArrayView::count(self : ArrayView[T], value : T) -> Int {
  for v in self; count = 0 {
    if v == value {
      continue count + 1
    }
    continue count
  } nobreak {
    count
  }
}

///|
/// Counts how many elements in the array view satisfy the predicate.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 2, 3, 4, 5][:]
///   inspect(view.count_if(x => x % 2 == 0), content="2")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::count_if(
  self : ArrayView[T],
  f : (T) -> Bool raise?,
) -> Int raise? {
  for v in self; count = 0 {
    if f(v) {
      continue count + 1
    }
    continue count
  } nobreak {
    count
  }
}

///|
/// Searches for the first occurrence of a value in the array view.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 2, 3, 2, 4][:]
///   debug_inspect(view.search(2), content="Some(1)")
///   debug_inspect(view.search(5), content="None")
/// }
/// ```
pub fn[T : Eq] ArrayView::search(self : ArrayView[T], value : T) -> Int? {
  for i, x in self {
    if x == value {
      break Some(i)
    }
  } nobreak {
    None
  }
}

///|
/// Searches for the first element in the view that satisfies the predicate
/// `f` and returns its (view-relative) index, or `None` if no element matches.
///
/// Example:
///
/// ```mbt check
/// test {
///   let view = [1, 2, 3, 4, 5][1:]
///   debug_inspect(view.search_by(x => x > 3), content="Some(2)")
///   debug_inspect(view.search_by(x => x > 99), content="None")
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::search_by(
  self : ArrayView[T],
  f : (T) -> Bool raise?,
) -> Int? raise? {
  for i, v in self {
    if f(v) {
      break Some(i)
    }
  } nobreak {
    None
  }
}

///|
/// Checks if the array view starts with the given prefix.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 2, 3, 4, 5][:]
///   inspect(view.starts_with([1, 2]), content="true")
///   inspect(view.starts_with([2, 3]), content="false")
/// }
/// ```
pub fn[T : Eq] ArrayView::starts_with(
  self : ArrayView[T],
  prefix : ArrayView[T],
) -> Bool {
  if prefix.length() > self.length() {
    return false
  }
  for i in 0.. Bool {
  let suffix_len = suffix.length()
  let self_len = self.length()
  if suffix_len > self_len {
    return false
  }
  for i in 0..)
///     ),
///   )
///   debug_inspect(v.strip_prefix([2, 3]), content="None")
/// }
/// ```
pub fn[T : Eq] ArrayView::strip_prefix(
  self : ArrayView[T],
  prefix : ArrayView[T],
) -> ArrayView[T]? {
  if self.starts_with(prefix) {
    Some(self[prefix.length():])
  } else {
    None
  }
}

///|
/// Returns a sub-view with `suffix` removed from the end, or `None` if the
/// view does not end with `suffix`. The returned view shares the original
/// backing array — no allocation.
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 2, 3, 4, 5][:]
///   debug_inspect(
///     v.strip_suffix([4, 5]),
///     content=(
///       #|Some()
///     ),
///   )
///   debug_inspect(v.strip_suffix([3, 4]), content="None")
/// }
/// ```
pub fn[T : Eq] ArrayView::strip_suffix(
  self : ArrayView[T],
  suffix : ArrayView[T],
) -> ArrayView[T]? {
  if self.ends_with(suffix) {
    Some(self[:self.length() - suffix.length()])
  } else {
    None
  }
}

///|
/// Performs a binary search on a sorted array view.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 3, 5, 7, 9][:]
///   debug_inspect(view.binary_search(5), content="Ok(2)")
///   debug_inspect(view.binary_search(6), content="Err(3)")
/// }
/// ```
pub fn[T : Compare] ArrayView::binary_search(
  self : ArrayView[T],
  value : T,
) -> Result[Int, Int] {
  let len = self.length()
  for i = 0, j = len; i < j; {
    let h = i + (j - i) / 2
    if self.unsafe_get(h) < value {
      continue h + 1, j
    } else {
      continue i, h
    }
  } nobreak {
    if i < len && self.unsafe_get(i) == value {
      Ok(i)
    } else {
      Err(i)
    }
  }
}

///|
/// Performs a binary search using a custom comparison function.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 3, 5, 7, 9][:]
///   let result = view.binary_search_by(x => x.compare(5))
///   debug_inspect(result, content="Ok(2)")
/// }
/// ```
#locals(cmp)
pub fn[T] ArrayView::binary_search_by(
  self : ArrayView[T],
  cmp : (T) -> Int raise?,
) -> Result[Int, Int] raise? {
  let len = self.length()
  for i = 0, j = len; i < j; {
    let h = i + (j - i) / 2
    if cmp(self.unsafe_get(h)) < 0 {
      continue h + 1, j
    } else {
      continue i, h
    }
  } nobreak {
    if i < len && cmp(self.unsafe_get(i)) == 0 {
      Ok(i)
    } else {
      Err(i)
    }
  }
}

///|
/// Fold out values from an ArrayView according to certain rules.
///
/// # Example
/// ```mbt check
/// test {
///   let sum = [1, 2, 3, 4, 5][:].fold(init=0, (sum, elem) => sum + elem)
///   inspect(sum, content="15")
/// }
/// ```
#locals(f)
pub fn[A, B] ArrayView::fold(
  self : ArrayView[A],
  init~ : B,
  f : (B, A) -> B raise?,
) -> B raise? {
  for x in self; acc = init {
    continue f(acc, x)
  } nobreak {
    acc
  }
}

///|
/// Fold out values from an ArrayView according to certain rules in reversed turn.
///
/// # Example
/// ```mbt check
/// test {
///   let sum = [1, 2, 3, 4, 5][:].rev_fold(init=0, (sum, elem) => sum + elem)
///   inspect(sum, content="15")
/// }
/// ```
#locals(f)
pub fn[A, B] ArrayView::rev_fold(
  self : ArrayView[A],
  init~ : B,
  f : (B, A) -> B raise?,
) -> B raise? {
  for i = self.length() - 1, acc = init; i >= 0; {
    continue i - 1, f(acc, self.unsafe_get(i))
  } nobreak {
    acc
  }
}

///|
/// Fold out values from an ArrayView according to certain rules with index.
///
/// # Example
/// ```mbt check
/// test {
///   let sum = [1, 2, 3, 4, 5][:].foldi(init=0, (index, sum, _elem) => sum + index)
///   inspect(sum, content="10")
/// }
/// ```
#locals(f)
pub fn[A, B] ArrayView::foldi(
  self : ArrayView[A],
  init~ : B,
  f : (Int, B, A) -> B raise?,
) -> B raise? {
  for i, x in self; acc = init {
    continue f(i, acc, x)
  } nobreak {
    acc
  }
}

///|
/// Fold out values from an ArrayView according to certain rules in reversed turn with index.
///
/// # Example
/// ```mbt check
/// test {
///   let sum = [1, 2, 3, 4, 5][:].rev_foldi(init=0, (index, sum, _elem) => {
///     sum + index
///   })
///   inspect(sum, content="10")
/// }
/// ```
#locals(f)
pub fn[A, B] ArrayView::rev_foldi(
  self : ArrayView[A],
  init~ : B,
  f : (Int, B, A) -> B raise?,
) -> B raise? {
  let len = self.length()
  for i in len>..0; acc = init {
    continue f(len - i - 1, acc, self.unsafe_get(i))
  } nobreak {
    acc
  }
}

///|
/// Maps a function over the elements of the array view.
///
/// # Example
/// ```mbt check
/// test {
///   let v = [3, 4, 5]
///   let v2 = v[1:].map(x => x + 1)
///   @test.assert_eq(v2, [5, 6])
/// }
/// ```
#locals(f)
pub fn[T, U] ArrayView::map(
  self : ArrayView[T],
  f : (T) -> U raise?,
) -> Array[U] raise? {
  let arr = Array::make_uninit(self.length())
  for i, x in self {
    arr.unsafe_set(i, f(x))
  }
  arr
}

///|
/// Maps a function over the elements of the array view with index.
///
/// # Example
/// ```mbt check
/// test {
///   let v = [3, 4, 5]
///   let v2 = v[1:].mapi((i, x) => x + i)
///   @test.assert_eq(v2, [4, 6])
/// }
/// ```
#locals(f)
pub fn[T, U] ArrayView::mapi(
  self : ArrayView[T],
  f : (Int, T) -> U raise?,
) -> Array[U] raise? {
  let arr = Array::make_uninit(self.length())
  for i, x in self {
    arr.unsafe_set(i, f(i, x))
  }
  arr
}

///|
/// Filters the array view with a predicate function.
///
/// # Example
/// ```mbt check
/// test {
///   let arr = [1, 2, 3, 4, 5, 6]
///   let v = arr[2:].filter(x => x % 2 == 0)
///   @test.assert_eq(v, [4, 6])
/// }
/// ```
#locals(f)
pub fn[T] ArrayView::filter(
  self : ArrayView[T],
  f : (T) -> Bool raise?,
) -> Array[T] raise? {
  let arr = []
  for v in self {
    if f(v) {
      arr.push(v)
    }
  }
  arr
}

///|
/// Apply a function to each element of the view, keeping only the `Some`
/// results. Returns a freshly allocated `Array`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let v = [1, 2, 3, 4, 5][1:4]
///   debug_inspect(
///     v.filter_map(x => if x % 2 == 0 { Some(x * 10) } else { None }),
///     content="[20, 40]",
///   )
/// }
/// ```
#locals(f)
pub fn[A, B] ArrayView::filter_map(
  self : ArrayView[A],
  f : (A) -> B? raise?,
) -> Array[B] raise? {
  let result = []
  for x in self {
    if f(x) is Some(x) {
      result.push(x)
    }
  }
  result
}

///|
/// Copy the view elements into a newly allocated `Array`.
///
/// # Example
/// ```mbt check
/// test {
///   let view = [1, 2, 3, 4, 5, 6][2:4]
///   let arr = view.to_owned()
///   @test.assert_eq(arr, [3, 4])
/// }
/// ```
#alias(to_array, deprecated)
pub fn[T] ArrayView::to_owned(self : ArrayView[T]) -> Array[T] {
  let len = self.length()
  if len == 0 {
    []
  } else {
    Array::unsafe_make_and_blit(
      self.buf(),
      allocate_len=len,
      src_offset=self.start(),
      len~,
    )
  }
}

///|
/// Concatenate strings within an array into a single complete string.
///
/// Example:
///
/// ```mbt check
/// test {
///   let a : Array[String] = ["1", "2", "3"]
///   let array_view = a[:]
///   inspect(array_view.join(","), content="1,2,3")
/// }
/// ```
pub fn[A : ToStringView] ArrayView::join(
  self : ArrayView[A],
  separator : StringView,
) -> String {
  match self {
    [] => ""
    [hd, .. tl] => {
      let hd = hd.to_string_view()
      let size_hint = for s in tl; size_hint = hd.length() {
        continue size_hint + s.to_string_view().length() + separator.length()
      } nobreak {
        size_hint
      }
      let size_hint = size_hint << 1
      let buf = StringBuilder(size_hint~)
      // buf.write_string(hd)
      buf.write_view(hd)
      if separator is "" {
        for s in tl {
          // buf.write_string(s)
          let s = s.to_string_view()
          buf.write_view(s)
        }
      } else {
        for s in tl {
          let s = s.to_string_view()
          buf.write_view(separator)
          // buf.write_string(s)
          buf.write_view(s)
        }
      }
      buf.to_string()
    }
  }
}

///|
/// Performs a lexicographical comparison of two array views.
///
/// This method compares the array views element by element until a difference
/// is found or one view is exhausted. Unlike the `Compare` trait implementation
/// which uses shortlex order (shorter views come first), this method compares
/// based purely on element values until a difference is found.
///
/// # Returns
///
/// - A negative integer if `self` is lexicographically less than `other`
/// - Zero if `self` is lexicographically equal to `other`
/// - A positive integer if `self` is lexicographically greater than `other`
///
/// # Example
///
/// ```mbt check
/// test {
///   inspect([1, 2][:].lexical_compare([1, 2, 3]), content="-1")
///   inspect([1, 2, 3][:].lexical_compare([1, 2]), content="1")
///   inspect([1, 2, 3][:].lexical_compare([1, 2, 3]), content="0")
///   inspect([1, 2, 3][:].lexical_compare([1, 2, 4]), content="-1")
/// }
/// ```
pub fn[T : Compare] ArrayView::lexical_compare(
  self : ArrayView[T],
  other : ArrayView[T],
) -> Int {
  let self_len = self.length()
  let other_len = other.length()
  let min_len = if self_len < other_len { self_len } else { other_len }
  for i in 0..
///     ),
///   )
/// }
/// ```
pub fn[T] ArrayView::rev(self : ArrayView[T]) -> Array[T] {
  let len = self.length()
  let arr = Array::make_uninit(len)
  for i in 0.. Bool {
  if self.length() != other.length() {
    return false
  }
  for i, x in self {
    if !(x == other.unsafe_get(i)) {
      return false
    }
  } nobreak {
    true
  }
}

///|
pub impl[T : Compare] Compare for ArrayView[T] with fn compare(self, other) -> Int {
  let len_self = self.length()
  let len_other = other.length()
  let cmp = len_self.compare(len_other)
  guard cmp == 0 else { return cmp }
  for i, x in self {
    let cmp = x.compare(other.unsafe_get(i))
    guard cmp == 0 else { break cmp }
  } nobreak {
    0
  }
}

///|
pub impl[A : Hash] Hash for ArrayView[A] with fn hash_combine(self, hasher) {
  for e in self {
    hasher.combine(e)
  }
}

///|
/// Concatenates two array views into a new view backed by a freshly allocated
/// array. The resulting view contains all elements from `self` followed by all
/// elements from `other`.
///
/// Note: although `+` looks cheap, this allocates a new owned `Array[T]`
/// because views cannot own data — the returned view spans the whole new
/// array.
///
/// Example:
///
/// ```mbt check
/// test {
///   let a = [1, 2, 3, 4, 5][1:4]
///   let b = [10, 20, 30][:2]
///   debug_inspect(
///     a + b,
///     content=(
///       #|
///     ),
///   )
/// }
/// ```
pub impl[T] Add for ArrayView[T] with fn add(self, other) {
  let len_self = self.length()
  let len_other = other.length()
  if len_self == 0 {
    Array::unsafe_make_and_blit(
      other.buf(),
      allocate_len=len_other,
      src_offset=other.start(),
      len=len_other,
    )
  } else {
    let result = Array::unsafe_make_and_blit(
      self.buf(),
      allocate_len=len_self + len_other,
      src_offset=self.start(),
      len=len_self,
    )
    UninitializedArray::unsafe_blit(
      result.buffer(),
      len_self,
      other.buf(),
      other.start(),
      len_other,
    )
    result
  }
}

// #endregion