Set Implicit Arguments. Require Import Arith. Require Import Lia. Require Import Recdef. (* The factorial function. *) Fixpoint fact n := match n with | 0 => 1 | S n => (S n) * (fact n) end. (* Helper for the inverse factorial. Since it is counting up, we have to manually prove that it terminates. For this, we prove that the distance between n and a (i.e., n - a) decreases with each recursive call. If a was zero it would loop forever, so we reject it. *) Function invfact_i n i a {measure (Nat.sub n) a} := if a =? 0 then None else match n ?= a with | Lt => None | Eq => Some (1 + i) | Gt => invfact_i n (1 + i) (a * S (S i)) end. Proof. intros. apply nat_compare_Gt_gt in teq0. apply beq_nat_false in teq. lia. Defined. (* The actual definition of the inverse factorial. *) Definition invfact n := invfact_i n 0 1. (* Convinience lemma that reverses a step of computation. *) Lemma invfact_back: forall n i a, a > 0 -> n > a -> invfact_i n (S i) (a * S (S i)) = invfact_i n i a. Proof. intros. rewrite (invfact_i_equation _ i). replace (a =? 0) with false by (symmetry; rewrite Nat.eqb_neq; lia). replace (n ?= a) with Gt by (symmetry; now rewrite <- nat_compare_gt). reflexivity. Qed. (* Lemma: if the inverse of a is n, then the inverse of (n+1)*a must be n+1. *) Lemma invfact_step: forall n a, n > 0 -> invfact a = Some n -> invfact (S n * a) = Some (S n). Proof. unfold invfact. intros n a H. apply invfact_i_ind; try easy; intros. - apply nat_compare_eq in e0. inversion H0. subst. rewrite invfact_i_equation. rewrite e. apply beq_nat_false in e. replace (S (S i) * a0 ?= a0) with Gt by (symmetry; apply nat_compare_gt; lia). rewrite invfact_i_equation. replace (a0 * S (S i) =? 0) with false by (symmetry; rewrite Nat.eqb_neq; lia). now rewrite Nat.mul_comm, Nat.compare_refl. - specialize (H0 H1). apply nat_compare_gt in e0. apply beq_nat_false in e. rewrite invfact_back in H0; auto; lia. Qed. (* Theorem: for all n > 0, the inverse of n! is n. *) Theorem invfact_correct: forall n, n > 0 -> invfact (fact n) = Some n. Proof. intros. induction n; [easy|]; destruct n; [easy|]. assert (S n > 0) by lia. specialize (IHn H0). replace (fact (S (S n))) with (S (S n) * (fact (S n))) by easy. now apply invfact_step. Qed. (* Theorem: our invfact will never produce a zero. *) Lemma invfact_i_zero: forall n i a, invfact_i n i a <> Some 0. Proof. intros. apply invfact_i_ind; easy. Qed. Theorem invfact_zero: forall n, invfact n <> Some 0. Proof. intros. apply invfact_i_zero. Qed. Lemma invfact_i_lt: forall n i a x, invfact_i n i a = Some x -> i < x. Proof. intros n i a. apply invfact_i_ind; try easy; intros. - inversion H. subst. lia. - specialize (H _ H0). lia. Qed. (* Theorem: If n is the inverse of both a and b, then a = b. *) Theorem invfact_only: forall n a b, invfact a = Some n -> invfact b = Some n -> a = b. Proof. unfold invfact. intros n a. apply invfact_i_ind; intros; try easy. - apply nat_compare_eq in e0. inversion H. subst. clear H. rewrite invfact_i_equation in H0. rewrite e in H0. destruct (b ?= a0) eqn:E. + now apply nat_compare_eq in E. + easy. + pose proof (invfact_i_lt _ _ _ H0). lia. - rewrite invfact_i_equation in H1. rewrite e in H1. destruct (b ?= a0) eqn:E. + inversion H1. subst. apply invfact_i_lt in H0. lia. + easy. + auto. Qed. (* From the previous theorem and invfact_correct, it easily follows that if the inverse of m is n, then m = n! *) Corollary invfact_fact: forall m n, invfact m = Some n -> m = fact n. Proof. intros. destruct n. - now apply invfact_zero in H. - assert (S n > 0) by lia. pose proof (invfact_correct H0). now apply invfact_only with (n := S n). Qed.