(** In order to state our example equality, we define an enumeration
    stream and a map operation for streams.
*)

CoFixpoint enumerate n : stream :=
  cons n (enumerate (S n)).CoFixpoint map f s : stream :=
  match s with cons n s' => cons (f n) (map f s') end.(** *** A proof using [cofix]

    We will now prove, using [cofix], that for any [n] the stream
    [enumerate n] is equal to the stream [cons n (map S (enumerate n))].
*)


(** First, we define an equality [seq] on streams.  Usually, one would
    do this as follows.

    [CoInductive seq : stream -> stream -> Prop :=] #<br>#
    [  | seq_fold : forall n s1 s2 (R : seq s1 s2), seq (cons n s1) (cons n s2).]

    Instead, we define the generating function, [seq_gen], beforehand,
    and then define [seq] as its greatest fixed point using
    [CoInductive].  The reason is simply that we need [seq_gen] later
    when using [paco].  If applying parameterized coinduction to an
    existing development where the generating function is not
    explicit, it can always easily be made explicit.

    Note that, albeit the use of [Inductive], the definition
    of [seq_gen] is not recursive.
*)

Inductive seq_gen seq : stream -> stream -> Prop :=
  | _seq_gen : forall n s1 s2 (R : seq s1 s2 : Prop), seq_gen seq (cons n s1) (cons n s2).(** *** A proof using [cofix]

    We will now prove, using [cofix], that for any [n] the stream
    [enumerate n] is equal to the stream [cons n (map S (enumerate n))].
*)


(** First, we define an equality [seq] on streams.  Usually, one would
    do this as follows.

    [CoInductive seq : stream -> stream -> Prop :=] #<br>#
    [  | seq_fold : forall n s1 s2 (R : seq s1 s2), seq (cons n s1) (cons n s2).]

    Instead, we define the generating function, [seq_gen], beforehand,
    and then define [seq] as its greatest fixed point using
    [CoInductive].  The reason is simply that we need [seq_gen] later
    when using [paco].  If applying parameterized coinduction to an
    existing development where the generating function is not
    explicit, it can always easily be made explicit.

    Note that, albeit the use of [Inductive], the definition
    of [seq_gen] is not recursive.
*)

Inductive seq_gen seq : stream -> stream -> Prop :=
  | _seq_gen : forall n s1 s2 (R : seq s1 s2 : Prop), seq_gen seq (cons n s1) (cons n s2).CoInductive seq : stream -> stream -> Prop :=
  | seq_fold : forall s1 s2, seq_gen seq s1 s2 -> seq s1 s2.CoInductive seq : stream -> stream -> Prop :=
  | seq_fold : forall s1 s2, seq_gen seq s1 s2 -> seq s1 s2.

(* Specifications: *)
Theorem example : forall n, seq (enumerate n) (cons n (map S (enumerate n))).