-
Notifications
You must be signed in to change notification settings - Fork 22
/
Function.v
138 lines (121 loc) · 4.24 KB
/
Function.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
From Coq Require Import PeanoNat Recdef Lia FunctionalExtensionality.
(** Counting the number of digits in binary representation. *)
(** Show directly defining this function with Fixpoint is not acceptable.
Error: Cannot guess decreasing argument of fix. *)
Fail Fixpoint attempt_1 (n r : nat) : nat :=
match n with
| 0 => r
| _ => attempt_1 (Nat.div2 n) (r + 1)
end.
(** function with decreasing measure. *)
Function attempt_2 (n r : nat) {measure (fun x => x) n} : nat :=
match n with
| 0 => r
| _ => attempt_2 (Nat.div2 n) (r + 1)
end.
Proof.
intros. apply Nat.lt_div2. lia.
Defined.
Compute attempt_2 1024 0.
(** function with well-founded relation. *)
Function attempt_3 (n r : nat) {wf lt n} : nat :=
match n with
| 0 => r
| _ => attempt_3 (Nat.div2 n) (r + 1)
end.
Proof.
+ intros. apply Nat.lt_div2. lia.
+ apply Wf_nat.lt_wf.
Defined.
Goal
attempt_2 8 0 = 4.
Proof.
(** use [xx_equation] to expand the recursive function. *)
rewrite attempt_2_equation; simpl.
rewrite attempt_2_equation; simpl.
rewrite attempt_2_equation; simpl.
rewrite attempt_2_equation; simpl.
rewrite attempt_2_equation; simpl.
reflexivity.
Qed.
(* note that the two above definitions are really just alternate ways of doing
the same thing ([measure] is just a convenience for leveraging the
well-foundedness of [nat]'s [lt]) *)
Theorem attempts_2_and_3_are_the_same :
attempt_2 = attempt_3.
Proof.
reflexivity.
Qed.
(** Now we'll redo the above using the standard library's [Fix] combinator,
which provides general well-founded recursion.
See CPDT's chapter http://adam.chlipala.net/cpdt/html/GeneralRec.html for an
excellent overview, including a better description of how this approach itself
works under the hood. *)
Check Fix.
(*
Fix
: forall (A : Type) (R : A -> A -> Prop),
well_founded R ->
forall P : A -> Type,
(forall x : A, (forall y : A, R y x -> P y) -> P x) ->
forall x : A, P x
To read this, start at the bottom: we're writing a function of type [forall (x:A), P
x]. The recursion is over [A] and decreases the relation [R], which must be
well-founded (think of [A = nat] and [R = lt] as examples). The body of the
function has the type
[forall (x:A) (F: forall (y:A), R y x -> P y), P x]
[x] is simply the argument to the body. The second argument is what I've called
[F] here: think of this as being the function being defined itself, in order to
support recursion. The catch is in its signature: instead of just taking [y:A],
it also takes [R y x]; that is, in order to call [F] recursively, you must pass
a proof that the argument used is smaller than the outer argument [x]. Because
[R] is well-founded, eventually this body must stop calling itself recursively.
*)
Lemma div2_smaller : forall n n',
n = S n' ->
Nat.div2 n < n.
Proof.
destruct n; intros.
discriminate.
apply Nat.lt_div2; lia.
Qed.
Definition numdigits (n: nat) : nat.
refine
(Fix Wf_nat.lt_wf (fun _ => nat)
(fun n (numdigits: forall (n':nat), n' < n -> nat) =>
(* unfortunately we need to convoy a proof to keep track of the
value of [n] when proving we decrease the argument to [numdigits] *)
match n as n0 return (n = n0 -> nat) with
| 0 => fun _ => 0
| S n' => fun H => 1 + numdigits (Nat.div2 n) _
end eq_refl) n).
eapply div2_smaller; eauto.
Defined.
(* We need to provide a wrapper around the standard library's [Fix_eq], which
describes the computational behavior of a [Fix] as being the same as a one-step
unfolding. However, the theorem also requires one to prove the body is unable to
distinguish between [F] arguments that are extensionally equal; this is true of
any Gallina code but can't be proven within Coq once and for all. *)
Theorem numdigits_eq : forall n,
numdigits n = match n with
| 0 => 0
| _ => 1 + numdigits (Nat.div2 n)
end.
Proof.
intros.
match goal with
| [ |- ?lhs = _ ] =>
match eval unfold numdigits in lhs with
| Fix ?wf ?P ?F _ =>
rewrite (Fix_eq wf P F)
end
end.
destruct n; reflexivity.
intros.
destruct x; auto.
Qed.
Example numdigits_5 : numdigits 8 = 4.
Proof.
repeat (rewrite numdigits_eq; cbn [Nat.div2 plus]).
reflexivity.
Qed.