/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Array.Basic
new_frontend

universes u v w

structure Subarray (α : Type u)  :=
(as : Array α) (start : Nat) (stop : Nat) (h₁ : start ≤ stop) (h₂ : stop ≤ as.size)

namespace Subarray

@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
let sz := USize.ofNat s.stop
let rec @[specialize] loop (i : USize) (b : β) : m β := do
  if i < sz then
    let a := s.as.uget i lcProof
    match (← f a b) with
    | ForInStep.done  b => pure b
    | ForInStep.yield b => loop (i+1) b
  else
    pure b
loop (USize.ofNat s.start) b

-- TODO: provide reference implementation
@[implementedBy Subarray.forInUnsafe]
constant forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
pure b

end Subarray

namespace Array
variables {α : Type u}

def toSubarray (as : Array α) (start stop : Nat) : Subarray α :=
if h₂ : stop ≤ as.size then
   if h₁ : start ≤ stop then
     { as := as, start := start, stop := stop, h₁ := h₁, h₂ := h₂ }
   else
     { as := as, start := stop, stop := stop, h₁ := Nat.leRefl _, h₂ := h₂ }
else
   if h₁ : start ≤ as.size then
     { as := as, start := start, stop := as.size, h₁ := h₁, h₂ := Nat.leRefl _ }
   else
     { as := as, start := as.size, stop := as.size, h₁ := Nat.leRefl _, h₂ := Nat.leRefl _ }

def ofSubarray (s : Subarray α) : Array α := do
let as := mkEmpty (s.stop - s.start)
for a in s do
  as := as.push a
return as

def extract (as : Array α) (start stop : Nat) : Array α :=
ofSubarray (as.toSubarray  start stop)

instance : HasCoe (Subarray α) (Array α) := ⟨ofSubarray⟩
instance : Coe (Subarray α) (Array α) := ⟨ofSubarray⟩

end Array
