||| The content of this module is based on the paper
||| Applications of Applicative Proof Search
||| by Liam O'Connor
|||
||| The main difference is that we use `Colist1` for the type of
||| generators rather than `Stream`. This allows us to avoid generating
||| many duplicates when dealing with finite types which are common in
||| programming (Bool) but even more so in dependently typed programming
||| (Vect 0, Fin (S n), etc.).

module Search.Generator

import Data.Colist
import Data.Colist1
import Data.Fin
import Data.List
import Data.Stream
import Data.Vect

------------------------------------------------------------------------
-- Interface

||| A generator for a given type is a non-empty colist of values of that
||| type.
public export
interface Generator a where
  generate : Colist1 a

------------------------------------------------------------------------
-- Implementations

-- Finite types

||| ALL of the natural numbers
public export
Generator Nat where
  generate = fromStream nats

||| ALL of the booleans
public export
Generator Bool where
  generate = True ::: [False]

||| ALL of the Fins
public export
{n : Nat} -> Generator (Fin (S n)) where
  generate = fromList1 (allFins n)

-- Polymorphic generators

||| We typically want to generate a generator for a unit-terminated right-nested
||| product so we have this special case that keeps the generator minimal.
public export
Generator a => Generator (a, ()) where
  generate = map (,()) generate

||| Put two generators together by exploring the plane they define.
||| This uses Cantor's zig zag traversal
public export
(Generator a, Generator b) => Generator (a, b) where
  generate = plane generate (\ _ => generate)

||| Put two generators together by exploring the plane they define.
||| This uses Cantor's zig zag traversal
public export
{0 b : a -> Type} -> (Generator a, (x : a) -> Generator (b x)) =>
  Generator (x : a ** b x) where
  generate = plane generate (\ x => generate)

||| Build entire vectors of values
public export
{n : Nat} -> Generator a => Generator (Vect n a) where
  generate = case n of
    Z => [] ::: []
    S n => map (uncurry (::)) generate

||| Generate arbitrary lists of values
||| Departing from the paper, to avoid having infinitely many copies of
||| the empty list, we handle the case `n = 0` separately.
public export
Generator a => Generator (List a) where
  generate = [] ::: forget (planeWith listy generate vectors) where
    listy : (n : Nat) -> Vect (S n) a -> List a
    listy _ = toList

    vectors : (n : Nat) -> Colist1 (Vect (S n) a)
    vectors n = generate
