0

Is it possible to "convert" a Fixpoint definition for the count function:

Fixpoint count (z: Z) (l: list Z) {struct l} : nat :=
  match l with
  | nil => 0%nat
  | (z' :: l') => if (Z.eq_dec z z')
                  then S (count z l')
                  else count z l'
  end.

To an Inductive predicate (I have my first attempt bellow, but I'm not sure if it is correct)? (This predicate is supposed to describe the relation between the function's input and output)

Inductive Count : nat -> list Z -> Z -> Prop :=
  | CountNil : forall (z: Z), Count 0 nil z
  | CountCons: forall (n: nat) (l0: list Z) (z: Z), Count n l0 z -> Count (S n) (cons z l0) z.

To find out if it's correct, I defined this Theorem (weak specification):

Theorem count_correct : forall (n: nat) (z: Z) (l: list Z), Count (count z l) l z.
Proof.
  intros.
  destruct l.
  - constructor.
  - ...

But I don't know how to complete it... Anyone can help?

1 Answers1

1

Your relation is incorrect because it is missing the case for when the head of your list is not the Z you're looking for. E.g. there is no term of type Count 0 [0] 1 even though count 1 [0] = 0. Add that, and while you're at it make the type more natural (order the arguments in the same way and also make z a parameter).

Inductive Count (z : Z) : list Z -> nat -> Prop :=
  | CountNil : Count z nil 0
  | CountYes : forall l n, Count z l n -> Count z (z :: l) (S n)
  | CountNo  : forall z' l n, z <> z' -> Count z l n -> Count z (z' :: l) n.

As for the correctness theorem, well, count is inductive on l, so probably so should be any theorem about it.

Theorem count_correct (z : Z) (n : N) (l : list Z) : Count z l (count z l).
Proof.
  intros.
  induction l as [ | z' l rec].
  - constructor.
  - cbn [count].
    destruct (Z.eq_dec z z') as [<- | no]; constructor; assumption.
Qed.

Do note that there's an automated mechanism to define Count and count_correct from count:

Require Import FunInd.

Function count (z : Z) (l : list Z) {struct l} : nat :=
  match l with
  | nil => 0
  | z' :: l =>
    if Z.eq_dec z z'
    then S (count z l)
    else count z l
  end.
  
Print R_count. (* Like Count *)
(* Inductive R_count (z : Z) : list Z -> nat -> Set :=
     R_count_0 : forall l : list Z, l = nil -> R_count z nil 0
  | R_count_1 : forall (l : list Z) (z' : Z) (l' : list Z),
    l = z' :: l' ->
    forall _x : z = z',
    Z.eq_dec z z' = left _x ->
    forall _res : nat,
    R_count z l' _res -> R_count z (z' :: l') (S _res)
  | R_count_2 : forall (l : list Z) (z' : Z) (l' : list Z),
    l = z' :: l' ->
    forall _x : z <> z',
    Z.eq_dec z z' = right _x ->
    forall _res : nat,
    R_count z l' _res -> R_count z (z' :: l') _res. *)
Check R_count_correct. (* : forall z l _res, _res = count z l -> R_count z l _res *)
Check R_count_complete. (* : forall z l _res, R_count z l _res -> _res = count z l *)
HTNW
  • 27,182
  • 1
  • 32
  • 60
  • Thank you so much for your explanation! But, since I'm kinda new to Coq, there something I don't understand - I know the syntax, but in `destruct (Z.eq_dec z z') as [<- | no]` that does `[<- | no]`mean? Is there any other way to write that? – Cris Teller May 24 '21 at 16:22
  • `destruct (Z.eq_dec z z') as [<- | no]; constructor; assumption.` = `destruct (Z.eq_dec z z') as [yes | no]. + rewrite <- yes. constructor. assumption. + constructor. * assumption. * assumption.` Putting `<-` or `->` instead of a variable name in an `intro`-pattern rewrites according to that variable in that direction and then deletes the variable all at once. [Please see the manual.](https://coq.inria.fr/refman/proof-engine/tactics.html#grammar-token-or_and_intropattern_loc) – HTNW May 24 '21 at 17:27
  • Thank you! I know what rewrite is but I haven't found anything on `yes` and `no`. Do they mean `yes` if `z==z'` and `no` otherwise? – Cris Teller May 24 '21 at 17:35
  • They are just variable names. I named them `yes` and `no` because they are the possible results of `dec`iding `eq`uality. You could rewrite `destruct (Z.eq_dec z z') as [yes | no].` as `refine (match Z.eq_dec z z' with left yes => _ | right no => _ end).` – HTNW May 24 '21 at 18:07
  • Yes, it's always better to name the variables, thank you for your explanation! – Cris Teller May 24 '21 at 19:39