// 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.

///|
/// External iterator type.
/// `Iterator[X]` is a mutable type: iterators internally maintain mutable state
/// to advance iteration.
/// All read operations on `Iterator` will advance the iterator,
/// and would give different result when called multiple times.
#alias(Iterator, deprecated="The name `Iterator` is deprecated, use `Iter` instead. Note that if you have defined `iterator()` method to support `for .. in` loop, you should also rename `iterator()` to `iter()`. See https://github.com/moonbitlang/core/pull/3127 for more details.")
struct Iter[X] {
  f : () -> X?
  mut size_hint : Int?
}

///|
priv enum IntersperseState[X] {
  IntersperseInit
  IntersperseElem(X)
  IntersperseSep
}

///|
/// Get the next element from an iterator, or return `None` if no more element exists.
/// The returned element will be consumed from the iterator.
/// Calling `next` repeatedly will iterate through all elements in the iterator.
#alias(peek, deprecated)
#alias(head)
pub fn[X] Iter::next(self : Iter[X]) -> X? {
  let result = (self.f)()
  match (result, self.size_hint) {
    (Some(_), Some(n)) =>
      self.size_hint = if n > 0 { Some(n - 1) } else { Some(0) }
    (None, _) => self.size_hint = Some(0)
    _ => ()
  }
  result
}

///|
/// Returns the hinted number of remaining elements if it is known.
///
/// The hint is intended to be exact for iterators produced by trusted
/// collection APIs and adapters, but it must not be used for correctness.
pub fn[X] Iter::size_hint(self : Iter[X]) -> Int? {
  self.size_hint
}

///|
#deprecated("Use Debug instead of Show for debugging purposes. See https://github.com/moonbitlang/core/blob/main/debug/README.mbt.md")
pub impl[X : Show] Show for Iter[X]

///|
pub impl[X : Show] Show for Iter[X] with fn output(self, logger) {
  logger.write_string("[")
  if self.next() is Some(x) {
    logger.write_object(x)
    while self.next() is Some(x) {
      logger.write_string(", ")
      logger.write_object(x)
    }
  }
  logger.write_string("]")
}

///|
pub impl[X : ToJson] ToJson for Iter[X] with fn to_json(self) {
  [
    for x in self => x
  ]
}

///|
/// Iterates over each element in the iterator, applying the function `f` to each element.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// - `self`: The iterator to consume.
/// - `f`: A function that takes an element of type `X` and returns `Unit`. This function is applied to each element of the iterator.
#locals(f)
pub fn[X] Iter::each(self : Iter[X], f : (X) -> Unit raise?) -> Unit raise? {
  while self.next() is Some(x) {
    f(x)
  }
}

///|
/// Return `true` if any element satisfies predicate `f`.
/// Function `any`.
#locals(f)
pub fn[X] Iter::any(self : Iter[X], f : (X) -> Bool) -> Bool {
  while self.next() is Some(x) {
    if f(x) {
      break true
    }
  } nobreak {
    false
  }
}

///|
/// Return `true` if all elements satisfy predicate `f`.
/// Function `all`.
#locals(f)
pub fn[X] Iter::all(self : Iter[X], f : (X) -> Bool) -> Bool {
  while self.next() is Some(x) {
    guard f(x) else { break false }
  } nobreak {
    true
  }
}

///|
/// Iterates over each element in the iterator, applying the function `f` to each element with index.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// - `self`: The iterator to consume.
/// - `f`: A function that takes an index of type `Int` and an element of type `X` and returns `Unit`. This function is applied to each element of the iterator.
#locals(f)
pub fn[X] Iter::eachi(
  self : Iter[X],
  f : (Int, X) -> Unit raise?,
) -> Unit raise? {
  let mut i = 0
  while self.next() is Some(x) {
    f(i, x)
    i += 1
  }
}

///|
/// Folds the elements of the iterator using the given function, starting with the given initial value.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
/// - `R`: The type of the accumulator (result) value.
///
/// # Arguments
///
/// - `self`: The iterator to consume.
/// - `f`: A function that takes an accumulator of type `R` and an element of type `X`, and returns a new accumulator value.
/// - `init`: The initial value for the fold operation.
///
/// # Returns
///
/// Returns the final accumulator value after folding all elements of the iterator.
#locals(f)
pub fn[X, R] Iter::fold(
  self : Iter[X],
  init~ : R,
  f : (R, X) -> R raise?,
) -> R raise? {
  let mut acc = init
  while self.next() is Some(x) {
    acc = f(acc, x)
  }
  acc
}

///|
/// Counts the number of elements in the iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// - `self`: The iterator to consume.
///
/// # Returns
///
/// Returns the number of elements in the iterator.
#alias(length)
pub fn[X] Iter::count(self : Iter[X]) -> Int {
  for _ in self; count = 0 {
    continue count + 1
  } nobreak {
    count
  }
}

///|
/// Counts the number of elements in the iterator that satisfy the predicate.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// - `self`: The iterator to consume.
/// - `f`: A predicate function applied to each element.
///
/// # Returns
///
/// Returns the number of elements for which `f` returns `true`.
#locals(f)
pub fn[X] Iter::count_if(self : Iter[X], f : (X) -> Bool) -> Int {
  for x in self; count = 0 {
    if f(x) {
      continue count + 1
    }
    continue count
  } nobreak {
    count
  }
}

// Producers

///|
/// Create a new iterator by supplying a `next` function directly.
/// The supplied function should output the next element being iterated
/// everytime it is called.
/// If the number of remaining elements is known, pass it as `size_hint`.
///
/// This function is intended for use by data structure authors,
/// and should not be called by end users in general.
#owned(f)
pub fn[X] Iter::new(f : () -> X?, size_hint? : Int) -> Iter[X] {
  let size_hint = match size_hint {
    Some(n) if n > 0 => Some(n)
    Some(_) => Some(0)
    None => None
  }
  { f, size_hint }
}

///|
/// Creates an empty iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Returns
///
/// Returns an empty iterator of type `Iter[X]`.
pub fn[X] Iter::empty() -> Iter[X] {
  Iter::new(() => None, size_hint=0)
}

///|
/// Creates an iterator that contains a single element.
///
/// # Type Parameters
///
/// - `X`: The type of the element in the iterator.
///
/// # Arguments
///
/// - `elem`: The single element to be contained in the iterator.
///
/// # Returns
///
/// Returns an iterator of type `Iter[X]` that contains the single element `a`.
pub fn[X] Iter::singleton(elem : X) -> Iter[X] {
  let mut consumed = false
  Iter::new(
    fn() {
      if consumed {
        None
      } else {
        consumed = true
        Some(elem)
      }
    },
    size_hint=1,
  )
}

///|
/// Creates an iterator that repeats the given element indefinitely.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// - `x`: The element to be repeated.
///
/// # Returns
///
/// Returns an iterator of type `Iter[X]` that repeats the element `x` indefinitely.
pub fn[X] Iter::repeat(x : X) -> Iter[X] {
  Iter::new(() => Some(x))
}

///|
/// Filters the elements of the iterator based on a predicate function.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The predicate function that determines whether an element should be included in the filtered iterator.
///
/// # Returns
///
/// A new iterator that only contains the elements for which the predicate function returns `IterContinue`.
///
/// # Note
/// The old iterator `self` must not be used again after calling `filter`.
pub fn[X] Iter::filter(self : Iter[X], f : (X) -> Bool) -> Iter[X] {
  Iter::new(fn() {
    while self.next() is Some(x) {
      if f(x) {
        break Some(x)
      }
    } nobreak {
      None
    }
  })
}

///|
/// Transforms the elements of the iterator using a mapping function.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
/// - `Y`: The type of the transformed elements.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The mapping function that transforms each element of the iterator.
///
/// # Returns
///
/// A new iterator that contains the transformed elements.
///
/// # Note
/// The old iterator `self` must not be used again after calling `map`.
pub fn[X, Y] Iter::map(self : Iter[X], f : (X) -> Y) -> Iter[Y] {
  {
    f: fn() {
      match self.next() {
        Some(x) => Some(f(x))
        None => None
      }
    },
    size_hint: self.size_hint,
  }
}

///|
/// Transforms the elements of the iterator using a mapping function.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
/// - `Y`: The type of the transformed elements.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The mapping function that transforms each element of the iterator with index.
///
/// # Returns
///
/// A new iterator that contains the transformed elements.
///
/// # Note
/// The old iterator `self` must not be used again after calling `mapi`.
pub fn[X, Y] Iter::mapi(self : Iter[X], f : (Int, X) -> Y) -> Iter[Y] {
  let mut i = 0
  {
    f: fn() {
      match self.next() {
        Some(x) => {
          let result = f(i, x)
          i += 1
          Some(result)
        }
        None => None
      }
    },
    size_hint: self.size_hint,
  }
}

///|
/// Transforms the elements of the iterator using a mapping function that returns an `Option`.
/// The elements for which the function returns `None` are filtered out.
///
/// The old iterator `self` must not be used again after calling `filter_map`.
pub fn[X, Y] Iter::filter_map(self : Iter[X], f : (X) -> Y?) -> Iter[Y] {
  Iter::new(fn() {
    while self.next() is Some(x) {
      guard f(x) is (Some(_) as y) else { () }
      break y
    } nobreak {
      None
    }
  })
}

///|
/// Transforms each element of the iterator into an iterator and flattens the resulting iterators into a single iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
/// - `Y`: The type of the transformed elements.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The function that transforms each element of the iterator into an iterator.
///
/// # Returns
///
/// A new iterator that contains the flattened elements.
///
/// # Note
/// The old iterator `self` and the iterators returned by `f`
/// must not be used again after calling `flat_map`.
pub fn[X, Y] Iter::flat_map(self : Iter[X], f : (X) -> Iter[Y]) -> Iter[Y] {
  let mut current_iter = Some(Iter::empty())
  Iter::new(fn() {
    guard current_iter is Some(iter) else { None }
    for x = iter.next() {
      match x {
        Some(_) as elem => break elem
        None => {
          guard self.next() is Some(x) else { break None }
          let iter = f(x)
          current_iter = Some(iter)
          continue iter.next()
        }
      }
    }
  })
}

///|
/// `iter.map(f).flatten() == iter.flat_map(f)`
pub fn[X] Iter::flatten(self : Iter[Iter[X]]) -> Iter[X] {
  self.flat_map(it => it)
}

///|
/// Collects the string-renderable elements of the iterator into a single
/// string, separated by `sep`.
/// The old iterator `self` must not be used again after calling `join`.
pub fn[A : ToStringView] Iter::join(self : Iter[A], sep : StringView) -> String {
  let result = StringBuilder()
  if self.next() is Some(x) {
    result.write_view(x.to_string_view())
    while self.next() is Some(x) {
      result.write_view(sep)
      result.write_view(x.to_string_view())
    }
  }
  result.to_string()
}

///|
/// Applies a function to each element of the iterator without modifying the iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The function to apply to each element of the iterator.
///
/// # Returns
///
/// The same iterator.
///
/// # Note
/// The old iterator `self` must not be used again after calling `tap`.
pub fn[X] Iter::tap(self : Iter[X], f : (X) -> Unit) -> Iter[X] {
  {
    f: fn() {
      let result = self.next()
      if result is Some(x) {
        f(x)
      }
      result
    },
    size_hint: self.size_hint,
  }
}

///|
/// Takes the first `n` elements from the iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `n` - The number of elements to take.
///
/// # Returns
///
/// A new iterator that contains the first `n` elements.
///
/// # Note
/// The old iterator `self` must not be used again after calling `take`.
pub fn[X] Iter::take(self : Iter[X], n : Int) -> Iter[X] {
  let mut remaining = n
  let size_hint = match self.size_hint {
    Some(_) if n <= 0 => Some(0)
    Some(len) if n < len => Some(n)
    Some(len) => Some(len)
    None if n <= 0 => Some(0)
    None => None
  }
  {
    f: fn() {
      guard remaining > 0 else { None }
      let result = self.next()
      if result is Some(_) {
        remaining -= 1
      }
      result
    },
    size_hint,
  }
}

///|
/// Takes elements from the iterator as long as the predicate function returns `true`.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The predicate function that determines whether an element should be taken.
///
/// # Returns
///
/// A new iterator that contains the elements as long as the predicate function returns `true`.
///
/// # Note
/// The old iterator `self` must not be used again after calling `take_while`.
pub fn[X] Iter::take_while(self : Iter[X], f : (X) -> Bool) -> Iter[X] {
  let mut still_running = true
  Iter::new(fn() {
    guard still_running else { None }
    let result = self.next()
    if result is Some(x) && !f(x) {
      still_running = false
      None
    } else {
      result
    }
  })
}

///|
/// Transforms the elements of the iterator using a mapping function upto the function returns `None`.
/// The old iterator `self` must not be used again after calling `map_while`.
pub fn[X, Y] Iter::map_while(self : Iter[X], f : (X) -> Y?) -> Iter[Y] {
  let mut still_running = true
  Iter::new(fn() {
    guard still_running else { None }
    let src = self.next()
    guard src is Some(x) else { None }
    let result = f(x)
    if result is None {
      still_running = false
    }
    result
  })
}

///|
/// Skips the first `n` elements from the iterator.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `n` - The number of elements to skip.
///
/// # Returns
///
/// A new iterator that starts after skipping the first `n` elements.
///
/// # Note
/// The old iterator `self` must not be used again after calling `drop`.
pub fn[X] Iter::drop(self : Iter[X], n : Int) -> Iter[X] {
  let mut remaining = n
  let size_hint = match self.size_hint {
    Some(len) if n <= 0 => Some(len)
    Some(len) if n < len => Some(len - n)
    Some(_) => Some(0)
    None => None
  }
  {
    f: fn() {
      while remaining > 0 {
        guard self.next() is Some(_) else { break None }
        remaining -= 1
      } nobreak {
        self.next()
      }
    },
    size_hint,
  }
}

///|
/// Skips elements from the iterator as long as the predicate function returns `true`.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The predicate function that determines whether an element should be skipped.
///
/// # Returns
///
/// A new iterator that starts after skipping the elements as long as the predicate function returns `true`.
///
/// # Note
/// The old iterator `self` must not be used again after calling `drop_while`.
pub fn[X] Iter::drop_while(self : Iter[X], f : (X) -> Bool) -> Iter[X] {
  let mut dropped = false
  Iter::new(fn() {
    if !dropped {
      dropped = true
      for x = self.next() {
        match x {
          Some(x) if f(x) => continue self.next()
          result => break result
        }
      }
    } else {
      self.next()
    }
  })
}

///|
/// Finds the first element in the iterator that satisfies the predicate function.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterator.
///
/// # Arguments
///
/// * `self` - The input iterator.
/// * `f` - The predicate function that determines whether an element is the first element to be found.
///
/// # Returns
///
/// An `Option` that contains the first element that satisfies the predicate function, or `None` if no such element is found.
///
/// # Note
/// The iterator `self` will advance past the returned element.
pub fn[X] Iter::find_first(self : Iter[X], f : (X) -> Bool) -> X? {
  while self.next() is Some(x) {
    if f(x) {
      break Some(x)
    }
  } nobreak {
    None
  }
}

///|
/// Combines two iterators into one by appending the elements of the second iterator to the first.
///
/// # Type Parameters
///
/// - `X`: The type of the elements in the iterators.
///
/// # Arguments
///
/// * `self` - The first input iterator.
/// * `other` - The second input iterator to be appended to the first.
///
/// # Returns
///
/// Returns a new iterator that contains the elements of `self` followed by the elements of `other`.
///
/// # Note
/// The old iterator `self` and `other` must not be used again after calling `tap`.
pub fn[X] Iter::concat(self : Iter[X], other : Iter[X]) -> Iter[X] {
  let mut in_first = true
  let size_hint = match (self.size_hint, other.size_hint) {
    (Some(n), Some(m)) => Some(n + m)
    _ => None
  }
  {
    f: fn() {
      if in_first {
        let result = self.next()
        if result is None {
          in_first = false
          other.next()
        } else {
          result
        }
      } else {
        other.next()
      }
    },
    size_hint,
  }
}

///|
/// Combines two iterators element-wise into an iterator of pairs.
///
/// The resulting iterator stops as soon as either input iterator is exhausted.
///
/// # Type Parameters
///
/// - `X`: The element type of `self`.
/// - `Y`: The element type of `other`.
///
/// # Arguments
///
/// * `self` - The first input iterator.
/// * `other` - The second input iterator.
///
/// # Returns
///
/// Returns a new iterator yielding tuples `(x, y)` where `x` comes from `self`
/// and `y` comes from `other`.
///
/// # Example
///
/// ```mbt check
/// test {
///   let numbers = (1).until(5)
///   let letters = ["a", "b", "c"].iter()
///   debug_inspect(
///     numbers.zip(letters).collect(),
///     content="[(1, \"a\"), (2, \"b\"), (3, \"c\")]",
///   )
/// }
/// ```
///
/// # Note
/// The old iterators `self` and `other` must not be used again after calling `zip`.
#alias(combine)
pub fn[X, Y] Iter::zip(self : Iter[X], other : Iter[Y]) -> Iter[(X, Y)] {
  let size_hint = match (self.size_hint, other.size_hint) {
    (Some(n), Some(m)) if n < m => Some(n)
    (Some(_), Some(m)) | ((Some(0), _) | (_, Some(0)) with m = 0) => Some(m)
    _ => None
  }
  {
    f: fn() {
      guard self.next() is Some(x) else { None }
      guard other.next() is Some(y) else { None }
      Some((x, y))
    },
    size_hint,
  }
}

///|
pub impl[T] Add for Iter[T] with fn add(self, other) {
  self.concat(other)
}

///|
/// Collects the elements of the iterator into an array.
/// The old iterator `self` must not be used again.
#alias(collect)
pub fn[X] Iter::to_array(self : Iter[X]) -> Array[X] {
  let result = match self.size_hint {
    Some(n) => Array::new(capacity=n)
    None => []
  }
  while self.next() is Some(x) {
    result.push(x)
  }
  result
}

///|
/// Return this iterator itself.
/// Return an iterator via `iter`.
#alias(iterator)
pub fn[X] Iter::iter(self : Iter[X]) -> Iter[X] {
  self
}

///|
/// Return an indexed view of this iterator.
/// Return an iterator via `iter2`.
#alias(iterator2)
pub fn[X] Iter::iter2(self : Iter[X]) -> Iter2[Int, X] {
  let mut i = 0
  Iter2({
    f: () => {
      guard self.next() is Some(elem) else { None }
      let result = Some((i, elem))
      i += 1
      result
    },
    size_hint: self.size_hint,
  })
}

///|
/// Returns the last element of the iterator, or `None` if the iterator is empty.
/// The old iterator `self` must not be used again after calling `last`.
pub fn[X] Iter::last(self : Iter[X]) -> X? {
  for x = (None : X?), y = self.next() {
    match (x, y) {
      (last, None) => break last
      (_, Some(_) as x) => continue x, self.next()
    }
  }
}

///|
/// Inserts a separator element `sep` between each element of the iterator.
///
/// # Parameters
///
/// - `self` : The iterator to intersperse the separator into.
/// - `sep` : The separator element to insert between each element of the iterator.
///
/// # Examples
///
/// ```mbt check
/// test {
///   let arr = []
///   [1, 2, 3].iter().intersperse(0).each(i => arr.push(i))
///   @test.assert_eq(arr, [1, 0, 2, 0, 3])
/// }
/// ```
///
/// # Note
/// The old iterator `self` must not be used again after calling `intersperse`.
pub fn[X] Iter::intersperse(self : Iter[X], sep : X) -> Iter[X] {
  let mut state : IntersperseState[X] = IntersperseInit
  let size_hint = match self.size_hint {
    Some(0) => Some(0)
    Some(n) => Some(n * 2 - 1)
    None => None
  }
  {
    f: fn() {
      match state {
        IntersperseInit => {
          let result = self.next()
          state = IntersperseSep
          result
        }
        IntersperseElem(x) => {
          state = IntersperseSep
          Some(x)
        }
        IntersperseSep =>
          // make sure we only output the separator when there is remaining element
          match self.next() {
            Some(x) => {
              state = IntersperseElem(x)
              Some(sep)
            }
            None => None
          }
      }
    },
    size_hint,
  }
}

///|
/// Return a sliced iterator view in range `[start, end)`.
/// Function `view`.
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn[X] Iter::view(self : Iter[X], start? : Int = 0, end? : Int) -> Iter[X] {
  match (start, end) {
    (_..=0, None) => self
    (_..=0, Some(end)) => self.take(end)
    (start, None) => self.drop(start)
    (start, Some(end)) => {
      let mut index = 0
      let size_hint = match self.size_hint {
        Some(_) if end <= start => Some(0)
        Some(len) if start >= len => Some(0)
        Some(len) if end < len => Some(end - start)
        Some(len) => Some(len - start)
        None if end <= start => Some(0)
        None => None
      }
      {
        f: fn() {
          if index >= end {
            return None
          }
          while index < start {
            guard self.next() is Some(_) else { return None }
            index += 1
          }
          if index >= end {
            return None
          }
          let result = self.next()
          if result is Some(_) {
            index += 1
          }
          result
        },
        size_hint,
      }
    }
  }
}

///|
/// Checks if the iterator contains an element equal to the given value.
///
/// Parameters:
///
/// * `self` : The iterator to search in.
/// * `value` : The value to search for.
///
/// Returns `true` if the iterator contains an element equal to the given value,
/// `false` otherwise.
///
/// Example:
///
/// ```mbt check
/// test {
///   let iter = [1, 2, 3, 4, 5].iter()
///   inspect(iter.contains(3), content="true")
///   inspect(iter.contains(6), content="false")
///   let iter = Iter::empty()
///   inspect(iter.contains(1), content="false")
/// }
/// ```
///
/// # Note
/// The old iterator `self` will advance past the searched element.
pub fn[X : Eq] Iter::contains(self : Iter[X], value : X) -> Bool {
  while self.next() is Some(x) {
    if x == value {
      break true
    }
  } nobreak {
    false
  }
}

///|
/// Returns the nth element of the iterator, or `None` if the iterator is
/// shorter than `n` elements.
/// The iterator `self` will advance past the returned element.
pub fn[X] Iter::nth(self : Iter[X], n : Int) -> X? {
  guard n >= 0 else { None }
  for _ in 0.. X? {
  guard self.next() is Some(x) else { return None }
  let mut res = x
  while self.next() is Some(x) {
    if x > res {
      res = x
    }
  }
  Some(res)
}

///|
/// Return the minimum element, or `None` if empty.
pub fn[X : Compare] Iter::minimum(self : Iter[X]) -> X? {
  guard self.next() is Some(x) else { return None }
  let mut res = x
  while self.next() is Some(x) {
    if x < res {
      res = x
    }
  }
  Some(res)
}

///|
/// This type is used for `for _, _ in ..` loop
/// (`for .. in` loop with two loop variables),
/// and should not be used directly in general.
#alias(Iterator2, deprecated="The name `Iterator2` is deprecated, use `Iter2` instead. Note that if you have defined `iterator2()` method to support `for .. in` loop, you should also rename `iterator2()` to `iter2()`. See https://github.com/moonbitlang/core/pull/3127 for more details.")
pub(all) struct Iter2[X, Y](Iter[(X, Y)])

///|
/// Construct an `Iter2` from a pair-producing function.
/// If the number of remaining pairs is known, pass it as `size_hint`.
#owned(f)
pub fn[X, Y] Iter2::new(f : () -> (X, Y)?, size_hint? : Int) -> Iter2[X, Y] {
  let size_hint = match size_hint {
    Some(n) if n > 0 => Some(n)
    Some(_) => Some(0)
    None => None
  }
  Iter2({ f, size_hint })
}

///|
/// Convert to plain iterator of pairs.
/// Return an iterator via `iter`.
#alias(iterator)
pub fn[X, Y] Iter2::iter(self : Iter2[X, Y]) -> Iter[(X, Y)] {
  self.0
}

///|
/// Return this two-variable iterator itself.
/// Return an iterator via `iter2`.
#alias(iterator2)
pub fn[X, Y] Iter2::iter2(self : Iter2[X, Y]) -> Iter2[X, Y] {
  self
}

///|
/// Get the next pair from the iterator.
pub fn[X, Y] Iter2::next(self : Iter2[X, Y]) -> (X, Y)? {
  self.0.next()
}

///|
#deprecated("Use Debug instead of Show for debugging purposes. See https://github.com/moonbitlang/core/blob/main/debug/README.mbt.md")
pub impl[X : Show, Y : Show] Show for Iter2[X, Y]

///|
#warnings("-deprecated")
pub impl[X : Show, Y : Show] Show for Iter2[X, Y] with fn output(self, logger) {
  self.0.output(logger)
}

///|
/// Apply callback to each pair.
pub fn[X, Y] Iter2::each(self : Iter2[X, Y], f : (X, Y) -> Unit) -> Unit {
  self.0.each(pair => f(pair.0, pair.1))
}

///|
/// Concatenate two `Iter2` streams.
pub fn[X, Y] Iter2::concat(
  self : Iter2[X, Y],
  other : Iter2[X, Y],
) -> Iter2[X, Y] {
  Iter2(self.0.concat(other.0))
}

///|
/// Collect all pairs into an array.
pub fn[X, Y] Iter2::to_array(self : Iter2[X, Y]) -> Array[(X, Y)] {
  self.0.to_array()
}