import Plausible.Gen import Plausible.Arbitrary open Plausible /-- Sized generators of type `α` such that `P : α -> Prop` holds for all generated values. -/ class ArbitrarySizedSuchThat (α : Type u) (P : α → Prop) where arbitrarySizedST : Nat → Gen α /-- Generators of type `α` such that `P : α -> Prop` holds for all generated values. -/ class ArbitrarySuchThat (α : Type u) (P : α → Prop) where arbitraryST : Gen α /-- Every `ArbitrarySizedSuchThat` instance can be automatically given a `ArbitrarySuchThat` instance -/ instance [ArbitrarySizedSuchThat α P] : ArbitrarySuchThat α P where arbitraryST := Gen.sized (ArbitrarySizedSuchThat.arbitrarySizedST P) namespace ArbitrarySizedSuchThat /-- Prints multiple samples from an `ArbitrarySizedSuchThat` instance. Usage: `ArbitrarySizedSuchThat.printSamples (fun n => Even n) 10` -/ def printSamples {α : Type} (P : α → Prop) [Repr α] [ArbitrarySizedSuchThat α P] (size : Nat) (numSamples : Nat := 10) : IO Unit := do for _ in [0:numSamples] do let sample ← Gen.run (arbitrarySizedST P size) size IO.println (repr sample) end ArbitrarySizedSuchThat /-- `ArbitrarySizedSuchThat` instance for equality propositions where a variable `x` is left-equal to some value `val`. (Note: `val` can be the result of a fully-applied function application, which is typically how this typeclass is used!) -/ instance {α : Type u} {val : α} : ArbitrarySizedSuchThat α (fun x => x = val) where arbitrarySizedST _ := return val /-- `ArbitrarySizedSuchThat` instance for equality propositions where a variable `x` is right-equal to some value `val`. (Note: `val` can be the result of a fully-applied function application, which is typically how this typeclass is used!) -/ instance {α : Type u} {val : α} : ArbitrarySizedSuchThat α (fun x => val = x) where arbitrarySizedST _ := return val instance {α : Type u} [Arbitrary α] : ArbitrarySizedSuchThat (α × α) (fun (l,r) => l = r) where arbitrarySizedST _ := do let a ← Arbitrary.arbitrary return (a,a) /-- `Arbitrary` instance for decidable predicates. It's expected that every (successfully) generated element for am `ArbitrarySuchThat α P` will satisfy `P`. We therefore try to return an element of the relevant subset type. -/ instance instArbitraryArbitrarySuchThatDec {α} {P : α → Prop} [DecidablePred P] [ArbitrarySuchThat _ P] : Arbitrary {x // P x} where arbitrary := do let x ← ArbitrarySuchThat.arbitraryST P if h : P x then return ⟨x, h⟩ else throw <| .genError "Generated element did not satisfy the predicate."