|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Lean FRO, LLC. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Paul Reichert |
| 5 | +-/ |
| 6 | +prelude |
| 7 | +import Init.Data.Nat.Lemmas |
| 8 | +import Init.RCases |
| 9 | +import Std.Data.Iterators.Basic |
| 10 | +import Std.Data.Iterators.Consumers.Monadic.Collect |
| 11 | +import Std.Data.Iterators.Consumers.Monadic.Loop |
| 12 | +import Std.Data.Iterators.Internal.Termination |
| 13 | +import Std.Data.Iterators.PostConditionMonad |
| 14 | + |
| 15 | +/-! |
| 16 | +This module provides the iterator combinator `IterM.takeWhile`. |
| 17 | +-/ |
| 18 | + |
| 19 | +namespace Std.Iterators |
| 20 | + |
| 21 | +variable {α : Type w} {m : Type w → Type w'} {n : Type w → Type w''} {β : Type w} |
| 22 | + |
| 23 | +@[unbox] |
| 24 | +structure TakeWhile (α : Type w) (m : Type w → Type w') (β : Type w) |
| 25 | + (P : β → PostconditionT m (ULift Bool)) where |
| 26 | + inner : IterM (α := α) m β |
| 27 | + |
| 28 | +/-- |
| 29 | +Given an iterator `it` and a predicate `P`, `it.takeWhile P` is an iterator that outputs |
| 30 | +the values emitted by `it` until one of those values violates `P`. |
| 31 | +If `P` is violated for some emitted value, the value is dropped and the iterator terminates. |
| 32 | +
|
| 33 | +**Marble diagram:** |
| 34 | +
|
| 35 | +Assuming that the predicate `P` accepts `a` and `b` but violates `c`: |
| 36 | +
|
| 37 | +```text |
| 38 | +it ---a----b---c--d-e--⊥ |
| 39 | +it.takeWhile P ---a----b---⊥ |
| 40 | +``` |
| 41 | +
|
| 42 | +**Termination properties:** |
| 43 | +
|
| 44 | +* `Finite` instance: only if `it` is finite |
| 45 | +* `Productive` instance: only if `it` is productive |
| 46 | +
|
| 47 | +Depending on `P`, it is possible `it.takeWhile P` is finite (or productive) although `it` is not. |
| 48 | +In this case, the `Finite` (or `Productive`) instance needs to be proved manually. |
| 49 | +
|
| 50 | +**Performance:** |
| 51 | +
|
| 52 | +This combinator calls `P` on each output of `it` until the predicate evaluates to false. Then |
| 53 | +it terminates. |
| 54 | +-/ |
| 55 | +@[inline] |
| 56 | +def IterM.takeWhileWithProof (P : β → PostconditionT m (ULift Bool)) (it : IterM (α := α) m β) := |
| 57 | + (toIterM (TakeWhile.mk (P := P) it) m β : IterM m β) |
| 58 | + |
| 59 | +inductive TakeWhile.PlausibleStep [Iterator α m β] {P} (it : IterM (α := TakeWhile α m β P) m β) : |
| 60 | + (step : IterStep (IterM (α := TakeWhile α m β P) m β) β) → Prop where |
| 61 | + | yield : ∀ {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out) → |
| 62 | + (P out).Property (.up true) → PlausibleStep it (.yield (it'.takeWhileWithProof P) out) |
| 63 | + | skip : ∀ {it'}, it.internalState.inner.IsPlausibleStep (.skip it') → |
| 64 | + PlausibleStep it (.skip (it'.takeWhileWithProof P)) |
| 65 | + | done : it.internalState.inner.IsPlausibleStep .done → PlausibleStep it .done |
| 66 | + | rejected : ∀ {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out) → |
| 67 | + (P out).Property (.up false) → PlausibleStep it .done |
| 68 | + |
| 69 | +@[always_inline, inline] |
| 70 | +instance TakeWhile.instIterator [Monad m] [Iterator α m β] {P} : |
| 71 | + Iterator (TakeWhile α m β P) m β where |
| 72 | + IsPlausibleStep := TakeWhile.PlausibleStep |
| 73 | + step it := do |
| 74 | + match ← it.internalState.inner.step with |
| 75 | + | .yield it' out h => match ← (P out).operation with |
| 76 | + | ⟨.up true, h'⟩ => pure <| .yield (it'.takeWhileWithProof P) out (.yield h h') |
| 77 | + | ⟨.up false, h'⟩ => pure <| .done (.rejected h h') |
| 78 | + | .skip it' h => pure <| .skip (it'.takeWhileWithProof P) (.skip h) |
| 79 | + | .done h => pure <| .done (.done h) |
| 80 | + |
| 81 | +private def TakeWhile.instFinitenessRelation [Monad m] [Iterator α m β] |
| 82 | + [Finite α m] {P} : |
| 83 | + FinitenessRelation (TakeWhile α m β P) m where |
| 84 | + rel := InvImage WellFoundedRelation.rel (IterM.finitelyManySteps ∘ TakeWhile.inner ∘ IterM.internalState) |
| 85 | + wf := by |
| 86 | + apply InvImage.wf |
| 87 | + exact WellFoundedRelation.wf |
| 88 | + subrelation {it it'} h := by |
| 89 | + obtain ⟨step, h, h'⟩ := h |
| 90 | + cases h' |
| 91 | + case yield it' out k h' h'' => |
| 92 | + cases h |
| 93 | + exact IterM.TerminationMeasures.Finite.rel_of_yield h' |
| 94 | + case skip it' out h' => |
| 95 | + cases h |
| 96 | + exact IterM.TerminationMeasures.Finite.rel_of_skip h' |
| 97 | + case done _ => |
| 98 | + cases h |
| 99 | + case rejected _ => |
| 100 | + cases h |
| 101 | + |
| 102 | +instance TakeWhile.instFinite [Monad m] [Iterator α m β] [Finite α m] {P} : |
| 103 | + Finite (TakeWhile α m β P) m := |
| 104 | + Finite.of_finitenessRelation instFinitenessRelation |
| 105 | + |
| 106 | +private def TakeWhile.instProductivenessRelation [Monad m] [Iterator α m β] |
| 107 | + [Finite α m] {P} : |
| 108 | + ProductivenessRelation (TakeWhile α m β P) m where |
| 109 | + rel := InvImage WellFoundedRelation.rel (IterM.finitelyManySkips ∘ TakeWhile.inner ∘ IterM.internalState) |
| 110 | + wf := by |
| 111 | + apply InvImage.wf |
| 112 | + exact WellFoundedRelation.wf |
| 113 | + subrelation {it it'} h := by |
| 114 | + cases h |
| 115 | + exact IterM.TerminationMeasures.Productive.rel_of_skip ‹_› |
| 116 | + |
| 117 | +instance TakeWhile.instProductive [Monad m] [Iterator α m β] [Finite α m] {P} : |
| 118 | + Productive (TakeWhile α m β P) m := |
| 119 | + Productive.of_productivenessRelation instProductivenessRelation |
| 120 | + |
| 121 | +instance TakeWhile.instIteratorCollect [Monad m] [Monad n] [Iterator α m β] [Productive α m] {P} : |
| 122 | + IteratorCollect (TakeWhile α m β P) m n := |
| 123 | + .defaultImplementation |
| 124 | + |
| 125 | +instance TakeWhile.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m β] {P} : |
| 126 | + IteratorCollectPartial (TakeWhile α m β P) m n := |
| 127 | + .defaultImplementation |
| 128 | + |
| 129 | +private def TakeWhile.PlausibleForInStep {β : Type u} {γ : Type v} |
| 130 | + (P : β → PostconditionT m (ULift Bool)) |
| 131 | + (f : β → γ → ForInStep γ → Prop) : |
| 132 | + β → γ → (ForInStep γ) → Prop |
| 133 | + | out, c, ForInStep.yield c' => (P out).Property (.up true) ∧ f out c (.yield c') |
| 134 | + | _, _, .done _ => True |
| 135 | + |
| 136 | +private def TakeWhile.wellFounded_plausibleForInStep {α β : Type w} {m : Type w → Type w'} |
| 137 | + [Monad m] [Iterator α m β] {γ : Type x} {P} |
| 138 | + {f : β → γ → ForInStep γ → Prop} (wf : IteratorLoop.WellFounded (TakeWhile α m β P) m f) : |
| 139 | + IteratorLoop.WellFounded α m (PlausibleForInStep P f) := by |
| 140 | + simp only [IteratorLoop.WellFounded] at ⊢ wf |
| 141 | + letI : WellFoundedRelation _ := ⟨_, wf⟩ |
| 142 | + apply Subrelation.wf |
| 143 | + (r := InvImage WellFoundedRelation.rel fun p => (p.1.takeWhileWithProof P, p.2)) |
| 144 | + (fun {p q} h => by |
| 145 | + simp only [InvImage, WellFoundedRelation.rel, this, IteratorLoop.rel, |
| 146 | + IterM.IsPlausibleStep, Iterator.IsPlausibleStep] |
| 147 | + obtain ⟨out, h, h'⟩ | ⟨h, h'⟩ := h |
| 148 | + · apply Or.inl |
| 149 | + exact ⟨out, .yield h h'.1, h'.2⟩ |
| 150 | + · apply Or.inr |
| 151 | + refine ⟨?_, h'⟩ |
| 152 | + exact PlausibleStep.skip h) |
| 153 | + apply InvImage.wf |
| 154 | + exact WellFoundedRelation.wf |
| 155 | + |
| 156 | +instance TakeWhile.instIteratorFor [Monad m] [Monad n] [Iterator α m β] |
| 157 | + [IteratorLoop α m n] [MonadLiftT m n] : |
| 158 | + IteratorLoop (TakeWhile α m β P) m n where |
| 159 | + forIn lift {γ} Plausible wf it init f := by |
| 160 | + refine IteratorLoop.forIn lift (γ := γ) |
| 161 | + (PlausibleForInStep P Plausible) |
| 162 | + (wellFounded_plausibleForInStep wf) |
| 163 | + it.internalState.inner |
| 164 | + init |
| 165 | + fun out acc => do match ← (P out).operation with |
| 166 | + | ⟨.up true, h⟩ => match ← f out acc with |
| 167 | + | ⟨.yield c, h'⟩ => pure ⟨.yield c, h, h'⟩ |
| 168 | + | ⟨.done c, h'⟩ => pure ⟨.done c, .intro⟩ |
| 169 | + | ⟨.up false, h⟩ => pure ⟨.done acc, .intro⟩ |
| 170 | + |
| 171 | +instance TakeWhile.instIteratorForPartial [Monad m] [Monad n] [Iterator α m β] |
| 172 | + [IteratorLoopPartial α m n] [MonadLiftT m n] {P} : |
| 173 | + IteratorLoopPartial (TakeWhile α m β P) m n where |
| 174 | + forInPartial lift {γ} it init f := do |
| 175 | + IteratorLoopPartial.forInPartial lift it.internalState.inner (γ := γ) |
| 176 | + init |
| 177 | + fun out acc => do match ← (P out).operation with |
| 178 | + | ⟨.up true, _⟩ => match ← f out acc with |
| 179 | + | .yield c => pure (.yield c) |
| 180 | + | .done c => pure (.done c) |
| 181 | + | ⟨.up false, _⟩ => pure (.done acc) |
| 182 | + |
| 183 | +end Std.Iterators |
0 commit comments