From 883a3807437add1caf5e06e630c952af8f74308f Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Wed, 12 Jul 2023 14:02:49 +0200 Subject: [PATCH 01/27] Changes for compatibility with 8.19 --- src/g_waterproof.mlg | 2 +- src/wp_rewrite.ml | 9 +++++---- theories/Waterprove.v | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/g_waterproof.mlg b/src/g_waterproof.mlg index 0fcf7bc7..934df556 100644 --- a/src/g_waterproof.mlg +++ b/src/g_waterproof.mlg @@ -56,7 +56,7 @@ let pname (s: string): ml_tactic_name = { mltac_plugin = "coq-core.plugins.coq-w (** Wrapper around {! Tac2env.define_primitive} to make easier the primitive definition *) let define_primitive (name: string) (arity: 'a arity) (f: 'a): unit = - Tac2env.define_primitive (pname name) (mk_closure arity f) + Tac2env.define_primitive (pname name) (mk_closure_val arity f) (** Defines a function of arity 0 (that only take a [unit] as an argument) diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 262e0b4c..55fa0d11 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -187,7 +187,7 @@ end = struct let empty = TDnet.empty let add (c:constr) (id:Ident.t) (dn:t) = - let (ctx, c) = Term.decompose_prod_assum c in + let (ctx, c) = Term.decompose_prod_decls c in let c = TDnet.pattern pat_of_constr c in TDnet.add dn c id @@ -229,8 +229,8 @@ let fresh_key: unit -> KerName.t = let decompose_applied_relation (env: Environ.env) (sigma: Evd.evar_map) (c: constr) (ctype: Evd.econstr) (left2right: bool): hypinfo option = let find_rel ty = - let sigma, ty = Clenv.make_evar_clause env sigma ty in - let (_, args) = Termops.decompose_app_vect sigma ty.Clenv.cl_concl in + let sigma, ty = EClause.make_evar_clause env sigma ty in + let (_, args) = EConstr.decompose_app sigma ty.EClause.cl_concl in let len = Array.length args in if 2 <= len then let c1 = args.(len - 2) in @@ -346,7 +346,8 @@ let find_applied_relation ?(loc: Loc.t option) (env: Environ.env) sigma c left2r ) let fill_rewrite_tab (env: Environ.env) (sigma: Evd.evar_map) (rule : raw_rew_rule) (rewrite_database: rewrite_db): rewrite_db = - let ist = Genintern.empty_glob_sign env in + let env = Global.env () in + let ist = Genintern.empty_glob_sign ~strict:true (Global.env ()) in let intern (tac: raw_generic_argument): glob_generic_argument = snd (Genintern.generic_intern ist tac) in let to_rew_rule ({CAst.loc;v=((c,ctx),b,t)}: raw_rew_rule): rew_rule = let sigma = Evd.merge_context_set Evd.univ_rigid sigma ctx in diff --git a/theories/Waterprove.v b/theories/Waterprove.v index 71881695..1c2b41e4 100644 --- a/theories/Waterprove.v +++ b/theories/Waterprove.v @@ -25,12 +25,12 @@ Require Import Ltac2.Init. Local Ltac2 Type database_type_ffi. -Local Ltac2 @ external database_type_main: unit -> database_type_ffi := "coq-waterproof" "database_type_main". -Local Ltac2 @ external database_type_decidability: unit -> database_type_ffi := "coq-waterproof" "database_type_decidability". -Local Ltac2 @ external database_type_shorten: unit -> database_type_ffi := "coq-waterproof" "database_type_shorten". +Local Ltac2 @ external database_type_main: unit -> database_type_ffi := "coq-core.plugins.coq-waterproof" "database_type_main". +Local Ltac2 @ external database_type_decidability: unit -> database_type_ffi := "coq-core.plugins.coq-waterproof" "database_type_decidability". +Local Ltac2 @ external database_type_shorten: unit -> database_type_ffi := "coq-core.plugins.coq-waterproof" "database_type_shorten". -Local Ltac2 @ external waterprove_ffi: int -> bool -> (unit -> constr) list -> database_type_ffi -> unit := "coq-waterproof" "waterprove". -Local Ltac2 @ external rwaterprove_ffi: int -> bool -> (unit -> constr) list -> database_type_ffi -> constr list -> constr list -> unit := "coq-waterproof" "rwaterprove". +Local Ltac2 @ external waterprove_ffi: int -> bool -> (unit -> constr) list -> database_type_ffi -> unit := "coq-core.plugins.coq-waterproof" "waterprove". +Local Ltac2 @ external rwaterprove_ffi: int -> bool -> (unit -> constr) list -> database_type_ffi -> constr list -> constr list -> unit := "coq-core.plugins.coq-waterproof" "rwaterprove". Ltac2 Type database_type := [ Main | Decidability | Shorten ]. From cbf5afaa44a5885f645e45006345ca34d13b2b07 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Wed, 12 Jul 2023 14:15:16 +0200 Subject: [PATCH 02/27] Adapt files for the build process --- .github/workflows/build.yml | 2 +- coq-waterproof.opam | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8e21b40..3d6a97d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - name: Build plugin uses: coq-community/docker-coq-action@v1 with: - custom_image: 'coqorg/coq:8.17-ocaml-4.14-flambda' + custom_image: 'coqorg/coq:dev-ocaml-4.14.1-flambda' opam_file: 'coq-waterproof.opam' before_script: | startGroup "Install dependencies" diff --git a/coq-waterproof.opam b/coq-waterproof.opam index 4af2c45c..4e337d7e 100644 --- a/coq-waterproof.opam +++ b/coq-waterproof.opam @@ -22,11 +22,11 @@ depends: [ build: [ ["autoreconf" "-i" "-s"] ["./configure"] - ["dune" "build"] + ["dune" "build" "-p" "coq-waterproof"] [make "-j%{jobs}%"] ] install: [ [make "install"] - ["dune" "install"] + ["dune" "install" "coq-waterproof"] ] From f2a9936d98774e993199338a39b3ec020eb9bea9 Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:09:43 +0200 Subject: [PATCH 03/27] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6017dce2..e561a45a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Firstly you should install [`opam`](https://opam.ocaml.org/). Then, you can create a new switch and install the requirements by running : ```bash -$ opam switch create waterproof --packages coq.8.17.0 +$ opam switch create waterproof --packages coq.8.19.0 $ eval $(opam env --switch=waterproof) ``` @@ -44,9 +44,9 @@ You can also install coq-waterproof without using opam (though it is greatly rec $ git clone https://github.com/impermeable/coq-waterproof.git && cd coq-waterproof $ autoreconf -i -s $ ./configure -$ dune build +$ dune build -p coq-waterproof $ make && make install # It is needed to compile and install with both `dune` and `make` -$ dune install +$ dune install coq-waterproof ``` ## Usage From 1c3fdc60fc217daa04147936e9ae0bb547903a4c Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Thu, 13 Jul 2023 10:58:47 +0200 Subject: [PATCH 04/27] Don't use opam to build and install for now --- coq-waterproof.opam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coq-waterproof.opam b/coq-waterproof.opam index 4e337d7e..5b701654 100644 --- a/coq-waterproof.opam +++ b/coq-waterproof.opam @@ -22,11 +22,11 @@ depends: [ build: [ ["autoreconf" "-i" "-s"] ["./configure"] - ["dune" "build" "-p" "coq-waterproof"] +# ["dune" "build" "-p" "coq-waterproof"] [make "-j%{jobs}%"] ] install: [ [make "install"] - ["dune" "install" "coq-waterproof"] +# ["dune" "install" "coq-waterproof"] ] From 44a0995f9f962520bcfbcb5d706f84d47e7822f9 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Fri, 14 Jul 2023 09:58:47 +0200 Subject: [PATCH 05/27] Adapt dune files to make build with dune possible --- coq-waterproof.opam | 8 ++------ dune-project | 6 +++--- theories/Automation/Framework.v | 3 ++- theories/Waterproof.v | 2 +- theories/dune | 4 +++- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/coq-waterproof.opam b/coq-waterproof.opam index 5b701654..7f08bb88 100644 --- a/coq-waterproof.opam +++ b/coq-waterproof.opam @@ -20,13 +20,9 @@ depends: [ ] build: [ - ["autoreconf" "-i" "-s"] - ["./configure"] -# ["dune" "build" "-p" "coq-waterproof"] - [make "-j%{jobs}%"] + ["dune" "build" "-p" "coq-waterproof"] ] install: [ - [make "install"] -# ["dune" "install" "coq-waterproof"] + ["dune" "install" "-p" "coq-waterproof"] ] diff --git a/dune-project b/dune-project index 804c2496..07dd8651 100644 --- a/dune-project +++ b/dune-project @@ -1,7 +1,7 @@ -(lang dune 3.2) -(using coq 0.3) +(lang dune 3.9) +(using coq 0.8) (package (name coq-waterproof) (synopsis "Coq proofs in a style that resembles non-mechanized mathematical proofs") - (depends coq-core)) \ No newline at end of file + (depends coq-core coq-stdlib)) \ No newline at end of file diff --git a/theories/Automation/Framework.v b/theories/Automation/Framework.v index d6f5f951..24727f57 100644 --- a/theories/Automation/Framework.v +++ b/theories/Automation/Framework.v @@ -16,5 +16,6 @@ (* *) (******************************************************************************) -Declare ML Module "coq-core.plugins.ltac2". +(* Declare ML Module "coq-core.plugins.ltac2". *) +From Ltac2 Require Import Init. Declare ML Module "waterproof:coq-waterproof.databases". \ No newline at end of file diff --git a/theories/Waterproof.v b/theories/Waterproof.v index a67e227f..644e59ae 100644 --- a/theories/Waterproof.v +++ b/theories/Waterproof.v @@ -16,5 +16,5 @@ (* *) (******************************************************************************) -Declare ML Module "coq-core.plugins.ltac2". +From Ltac2 Require Import Init. Declare ML Module "waterproof:coq-waterproof.plugin". \ No newline at end of file diff --git a/theories/dune b/theories/dune index 23cbe62b..917f06b0 100644 --- a/theories/dune +++ b/theories/dune @@ -1,6 +1,8 @@ (coq.theory (name coq_waterproof) + (package coq-waterproof) (flags :standard) - (libraries coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2)) + (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2) + (theories Ltac2)) (include_subdirs qualified) \ No newline at end of file From c388f431f067714c3f88a6f95511edcbfb7e91ea Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Fri, 14 Jul 2023 11:56:56 +0200 Subject: [PATCH 06/27] Change name field in dune file to Waterproof --- theories/dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theories/dune b/theories/dune index 917f06b0..41bb14e2 100644 --- a/theories/dune +++ b/theories/dune @@ -1,5 +1,5 @@ (coq.theory - (name coq_waterproof) + (name Waterproof) (package coq-waterproof) (flags :standard) (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2) From c7badb4dedad62e17716098361a7d09b5ac5f6d8 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Fri, 14 Jul 2023 16:14:38 +0200 Subject: [PATCH 07/27] Revert to dune language 3.6 --- dune-project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index 07dd8651..2a0bccc2 100644 --- a/dune-project +++ b/dune-project @@ -1,5 +1,5 @@ -(lang dune 3.9) -(using coq 0.8) +(lang dune 3.6) +(using coq 0.6) (package (name coq-waterproof) From 2bb40c89833cd44564d66101d2a262157e5c3284 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Fri, 14 Jul 2023 16:29:39 +0200 Subject: [PATCH 08/27] Remove line on theory Ltac2 --- theories/dune | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/theories/dune b/theories/dune index 41bb14e2..313d2078 100644 --- a/theories/dune +++ b/theories/dune @@ -2,7 +2,6 @@ (name Waterproof) (package coq-waterproof) (flags :standard) - (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2) - (theories Ltac2)) + (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2)) (include_subdirs qualified) \ No newline at end of file From 2d6557016ab84cf2e14d8f6e1fc7dabbe9094651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Tue, 19 Sep 2023 12:32:37 +0200 Subject: [PATCH 09/27] Adapt to coq/coq#17836 (sort poly) --- src/wp_rewrite.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 55fa0d11..2889b2d9 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -176,7 +176,7 @@ end = struct let a = ca.(len - 1) in let ca = Array.sub ca 0 (len - 1) in Some (DApp, [mkApp (f, ca); a]) - | Proj (p,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) + | Proj (p,_,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) | Int i -> Some (DInt i, []) | Float f -> Some (DFloat f, []) | Array (_u,t,def,ty) -> Some (DArray, Array.to_list t @ [def ; ty]) @@ -281,6 +281,7 @@ let one_base (where: variable option) (tactic: trace tactic) (rewrite_database: Proofview.Goal.enter begin fun gl -> let sigma = Proofview.Goal.sigma gl in let subst, ctx' = UnivGen.fresh_universe_context_set_instance rule.rew_ctx in + let subst = Sorts.QVar.Map.empty, subst in let c' = Vars.subst_univs_level_constr subst rule.rew_lemma in let sigma = Evd.merge_context_set Evd.univ_flexible sigma ctx' in Proofview.tclTHEN (Proofview.Unsafe.tclEVARS sigma) (rewrite rule.rew_l2r c' tac) @@ -383,7 +384,7 @@ let to_raw_rew_rule (env: Environ.env) (sigma: Evd.evar_map) (hyp: Constrexpr.co let econstr, context = Constrintern.interp_constr env sigma hyp in let constr = EConstr.to_constr sigma econstr in let univ_ctx = UState.context_set context in - let ctx = (DeclareUctx.declare_universe_context ~poly:false univ_ctx; Univ.ContextSet.empty) in + let ctx = (Global.push_context_set ~strict:true univ_ctx; Univ.ContextSet.empty) in CAst.make ?loc:(Constrexpr_ops.constr_loc hyp) ((constr, ctx), true, Option.map (in_gen (rawwit wit_ltac)) None) (** From e989f9cdec12725eefbc7ae4ca6a74c429edd980 Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:30:17 +0200 Subject: [PATCH 10/27] Revert "Adapt to coq/coq#17836 (sort poly)" --- src/wp_rewrite.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 2889b2d9..55fa0d11 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -176,7 +176,7 @@ end = struct let a = ca.(len - 1) in let ca = Array.sub ca 0 (len - 1) in Some (DApp, [mkApp (f, ca); a]) - | Proj (p,_,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) + | Proj (p,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) | Int i -> Some (DInt i, []) | Float f -> Some (DFloat f, []) | Array (_u,t,def,ty) -> Some (DArray, Array.to_list t @ [def ; ty]) @@ -281,7 +281,6 @@ let one_base (where: variable option) (tactic: trace tactic) (rewrite_database: Proofview.Goal.enter begin fun gl -> let sigma = Proofview.Goal.sigma gl in let subst, ctx' = UnivGen.fresh_universe_context_set_instance rule.rew_ctx in - let subst = Sorts.QVar.Map.empty, subst in let c' = Vars.subst_univs_level_constr subst rule.rew_lemma in let sigma = Evd.merge_context_set Evd.univ_flexible sigma ctx' in Proofview.tclTHEN (Proofview.Unsafe.tclEVARS sigma) (rewrite rule.rew_l2r c' tac) @@ -384,7 +383,7 @@ let to_raw_rew_rule (env: Environ.env) (sigma: Evd.evar_map) (hyp: Constrexpr.co let econstr, context = Constrintern.interp_constr env sigma hyp in let constr = EConstr.to_constr sigma econstr in let univ_ctx = UState.context_set context in - let ctx = (Global.push_context_set ~strict:true univ_ctx; Univ.ContextSet.empty) in + let ctx = (DeclareUctx.declare_universe_context ~poly:false univ_ctx; Univ.ContextSet.empty) in CAst.make ?loc:(Constrexpr_ops.constr_loc hyp) ((constr, ctx), true, Option.map (in_gen (rawwit wit_ltac)) None) (** From f2fede8b0fbce29a7af312d9b8c28f3f2538b032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Fri, 20 Oct 2023 15:41:00 +0200 Subject: [PATCH 11/27] Adapt to coq/coq#18174 (Clenv.unify takes cv_pb) --- src/wp_eauto.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp_eauto.ml b/src/wp_eauto.ml index cc308155..51716f94 100644 --- a/src/wp_eauto.ml +++ b/src/wp_eauto.ml @@ -43,7 +43,7 @@ let e_give_exact ?(flags: Unification.unify_flags = eauto_unif_flags) (c: types) if occur_existential sigma t1 || occur_existential sigma t2 then Tacticals.tclTHENLIST [ Unsafe.tclEVARS sigma; - Clenv.unify ~flags t1; + Clenv.unify ~cv_pb:CUMUL ~flags t1; exact_no_check c ] else exact_check c @@ -63,7 +63,7 @@ let e_assumption: trace tactic = let t = Declaration.get_type decl in begin if not_ground || occur_existential sigma t - then Clenv.unify ~flags:eauto_unif_flags t <*> exact_no_check (mkVar id) + then Clenv.unify ~cv_pb:CUMUL ~flags:eauto_unif_flags t <*> exact_no_check (mkVar id) else exact_check (mkVar id) end <*> tclUNIT @@ singleton_trace true (str "eassumption") (str "") in @@ -397,4 +397,4 @@ let wp_eauto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (db_ This function acts the same as {! wp_eauto} but will fail if all proof found contain at least one must-use lemma that is unused or one hint that is in the [forbidden] list. *) let rwp_eauto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (dbnames: hint_db_name list) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): trace tactic = - gen_wp_eauto log ~n lems (Some dbnames) must_use_tactics forbidden_tactics \ No newline at end of file + gen_wp_eauto log ~n lems (Some dbnames) must_use_tactics forbidden_tactics From a33360166b73f8864b41725eeddcdadf788de819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Tue, 7 Nov 2023 08:22:12 +0100 Subject: [PATCH 12/27] Adapt to coq/coq#17836 (sort poly) (#28) --- src/wp_rewrite.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 55fa0d11..2889b2d9 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -176,7 +176,7 @@ end = struct let a = ca.(len - 1) in let ca = Array.sub ca 0 (len - 1) in Some (DApp, [mkApp (f, ca); a]) - | Proj (p,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) + | Proj (p,_,c) -> pat_of_constr @@ mkApp (mkConst @@ Projection.constant p, [|c|]) | Int i -> Some (DInt i, []) | Float f -> Some (DFloat f, []) | Array (_u,t,def,ty) -> Some (DArray, Array.to_list t @ [def ; ty]) @@ -281,6 +281,7 @@ let one_base (where: variable option) (tactic: trace tactic) (rewrite_database: Proofview.Goal.enter begin fun gl -> let sigma = Proofview.Goal.sigma gl in let subst, ctx' = UnivGen.fresh_universe_context_set_instance rule.rew_ctx in + let subst = Sorts.QVar.Map.empty, subst in let c' = Vars.subst_univs_level_constr subst rule.rew_lemma in let sigma = Evd.merge_context_set Evd.univ_flexible sigma ctx' in Proofview.tclTHEN (Proofview.Unsafe.tclEVARS sigma) (rewrite rule.rew_l2r c' tac) @@ -383,7 +384,7 @@ let to_raw_rew_rule (env: Environ.env) (sigma: Evd.evar_map) (hyp: Constrexpr.co let econstr, context = Constrintern.interp_constr env sigma hyp in let constr = EConstr.to_constr sigma econstr in let univ_ctx = UState.context_set context in - let ctx = (DeclareUctx.declare_universe_context ~poly:false univ_ctx; Univ.ContextSet.empty) in + let ctx = (Global.push_context_set ~strict:true univ_ctx; Univ.ContextSet.empty) in CAst.make ?loc:(Constrexpr_ops.constr_loc hyp) ((constr, ctx), true, Option.map (in_gen (rawwit wit_ltac)) None) (** From aec4fbe2a9b586b246e18b00a52c66ee7a772e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Wed, 15 Nov 2023 13:09:49 +0100 Subject: [PATCH 13/27] Adapt to coq/coq#18280 (case relevance outside case info) (#37) --- src/wp_rewrite.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 2889b2d9..7d058dda 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -164,7 +164,7 @@ end = struct | Construct (c,u) -> Some (DRef (ConstructRef c), []) | Meta _ -> assert false | Evar (i,_) -> None - | Case (ci,u1,pms1,c1,_iv,c2,ca) -> Some (DCase(ci), [snd c1; c2] @ Array.map_to_list snd ca) + | Case (ci,u1,pms1,(c1,_),_iv,c2,ca) -> Some (DCase(ci), [snd c1; c2] @ Array.map_to_list snd ca) | Fix ((ia,i),(_,ta,ca)) -> Some (DFix(ia,i), Array.to_list ta @ Array.to_list ca) | CoFix (i,(_,ta,ca)) -> Some (DCoFix(i), Array.to_list ta @ Array.to_list ca) | Cast (c,_,_) -> pat_of_constr c From 865d59eb8f47f6535a145dbf1bce20427007203e Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Thu, 4 Jan 2024 17:13:49 +0100 Subject: [PATCH 14/27] Merge features of version 2.1.1 into coq-master (#46) * [build] Initial port to Dune This was done at the interest of Vincent WENDLING for jsCoq use. The setup is fairly standard, other than excluding the deprecated dir (which doesn't compile) * Update README.md * Update dune file in theories * Change importing Ltac2 modules and build only with dune * Update version numbers * Restore changes file and rename license file * Fix metadata in files * Add template tags * Add @install, minimal dune version, dev-repo to opam file * Added infrastructure for enforcing users to mention use of definitions. * Removed some unnecessary lemma's and corresponding tactics (that were apparently throwing errors anyway). * Restored old lemmas in 'SupAndInf' necessary for keeping limsup file working. To be rewritten in future. * Moved tests in SupAndInf.v file to separate test file. Small bug fix in rewrite rule sup and infimum. * 'We need to show'-tactic now also accepts equivalent goals. Added 'By ...' clause to allow for an additional lemma from which equivalence follows. * Replaced use of 'it suffices to show' by 'we need to show' for unfolding definitions. * Added testcases new behaviour tactic. * Improved feedback given new restricted proof automation. * Updated documentation. * Small change in error message. * Moved 'AutomationFailure' exception type to 'Wateprove' file. * Improved feedback 'ItHolds' for new restricted proof automation. * Replaced custom error shielded goal by standard error that can be caught using Ltac2. * Added 'Since ...' clause as alternative to 'By ...' that accepts statements instead of references. (No 'Since ... we need to show ...' because the 'By'-version is to be removed soon. ) * Removed acceptation equivalent goals from 'We need to show' tactic. * Improved feedback 'By ... we conclude ...' for new restricted automated proof finding. Added 'Since ... we conclude that ...' alternative. * Improved feedback, now it says which part of a chain of (in)equalities it failed to prove. Refactored checking whether additional lemma is actually used: now check is done in 'Waterprove.v', it throws errors with relevant information that other tactics use to display user-readable errors. * 'Contradiction' now tries to find a contradiction to the previous statement. * Moved 'Obtain' tactic to separate file. * Simplified old notation 'Obtain' tactic. Now it automatically tries to destruct previous statement and users no longer need to specify the corresponding property. * Simplified names for hypotheses not labeled by user. * Added 'change' to 'Expand definition', so putting in different dummy variables actually has effect. * Added StateGoal wrappers to subgoals of non-natural induction. * Added workaround for unexpected anomalies in restricted automation. * Strengthened workaround. Both to prevent more anomalies with hypotheses and to prevent endless searching for proof with hypothesis because it is used implicitly by the 'assumption' tactic. * Replaced axiomatic definitions with locked ones. Also strengthened shielded automation to depth 3 to be able to use definition supremum.. * Fixed dune build. * feat: add warnings * refactor: change the names of the warning functions * feat: add error function * Add files to _CoqProject.in * feat: use the warning in Conclusion.v * feat: deal with Rabs Rmax Rmin more easily by destructing them * Readded shortened 'Obtain accoriding to ...' tactic. * Reworked shielding to use Shorten database type. Includes new tactics for explicitly unfolding definitions, optimized for speed automation (i.e. they explicitly fail to prevent large growth search tree). * Improved expanding def for sup and inf. * Implemented user-level error throwing in tactics. * Fixed small error from merge. * Disabled debug mode automation. * Reverted back to old SupAndInf.v file. * Replaced wrapping after 'Expand the definition of ...' by throwing errors suggesting user to replace the line by a tactic with a similar effect as unfolding the definitions. (Also slightly simplified notation base case natural induction.) * Added tactic for unfolding that prints a message instead of throwing an errror. For internal use by Waterproof editor only. * chore: bump version number * fix: add internal unfold for general terms and tests for internal unfold * Hint fixes (#30) fix: change hint priorities * Automation debug (#31) * fix: change hint priorities * fix: change hint priorities * fix: fight line endings * feat: add switch for debugging of the automation * feat: Add description on how to enable debugging to readme * feat: use N1 instead of N in definition convergence * Improve either (#32) * fix: definition of divergence to infinity and min infinity * feat: add double_is_even to wp_integers rather than subsequences * feat: let either work with uninformative or if the goal is a prop * feat: also separate either code for prop for three statements. Refactor some decidability statements * refactor: use new either code in alternative characterization of continuity in R * feat: add alternative characterization continuity for natural numbers as subset of metric space of reals * feat: some adaptations for the continuity exercises * chore: update changelog and bump version number in opam file * Tactics for using strong induction to define index sequence (#33) * Added strong induction for defining index sequence. Warning: uses fixed letters 'n' and 'k' for index sequence and induction variable. * Removed 'Check' and 'Print's. * Removed old code. * Show version number (#34) * feat: Add tactic to show version number * fix: Add version file to _CoqProject.in * Allow testing against a folder with dune's runtest and set version number (#35) * feat: add exercises as dune test * feat: add python script for testing * feat: abstract the testing scripts and deal with errors better * fix: call the correct test script * Set Help to use default automation system. (#36) * Change required version number * Try to fix coq requirement * Fix for problems with strong induction for defining index sequence. (#38) * Fix for problems with strong induction for defining subsequence. * Updated formatting goal wrapper. * fix: some small fixes to be compatible with dev * fix: change order fold_right arguments in index sequence * fix: Small changes to the sequences and subsequences files because autorewrite no longer seems to work as before * chore: Change version number * try to allow for dev version * fix: try with version numbers * fix: try to fix version numbers * fix: Remove unnecessary import in Sequences.v * feat: add logging sentence for wp_autorewrite * feat: add logging sentence for wp_autorewrite (#43) * feat: create option to print rewrite hints (#44) * fix: Fix autorewrite (the env variable didn't come through properly) * fix: Compatibility with compilers >= 4.09.0 (#45) * fix: Exclude s390x architecture * refactor: put *.install and *.diags in gitignore --------- Co-authored-by: Emilio Jesus Gallego Arias Co-authored-by: Jelle --- .gitignore | 4 +- CHANGES.md | 43 ++ LICENCE => LICENSE | 0 README.md | 12 +- _CoqProject.in | 19 +- coq-waterproof.opam | 31 +- dune-project | 3 +- src/META.coq-waterproof | 2 +- src/exceptions.ml | 16 +- src/exceptions.mli | 13 + src/g_waterproof.mlg | 45 ++ src/hint_dataset.ml | 2 +- src/hint_dataset_declarations.ml | 33 +- src/hint_dataset_declarations.mli | 3 +- src/proofutils.mli | 2 - src/waterprove.ml | 49 +- src/waterprove.mli | 10 + src/wp_rewrite.ml | 6 +- src/wp_rewrite.mli | 1 + tests/automation/Chains.v | 82 +++ tests/automation/Shield.v | 2 +- tests/automation/databases/Core.v | 16 +- tests/automation/databases/Decidability.v | 58 +- tests/automation/databases/Empty.v | 1 + tests/automation/databases/Integers.v | 30 +- tests/automation/databases/RealsAndIntegers.v | 32 +- tests/dune | 8 + .../Analysis/StrongInductionIndexSequence.v | 94 +++ tests/libs/Negation.v | 6 +- tests/tactics/BothDirections.v | 2 +- tests/tactics/BothStatements.v | 4 +- tests/tactics/Choose.v | 4 +- tests/tactics/Conclusion.v | 92 ++- tests/tactics/Contradiction.v | 34 +- tests/tactics/Either.v | 63 +- tests/tactics/Induction.v | 12 +- tests/tactics/ItHolds.v | 170 ++++++ tests/tactics/ItSuffices.v | 42 ++ tests/tactics/{ChooseSuchThat.v => Obtain.v} | 70 +-- tests/tactics/Take.v | 8 +- tests/tactics/ToShow.v | 16 +- tests/tactics/Unfold.v | 184 ++++-- tests/test-folder.py | 41 ++ tests/util/MessagesToUser.v | 27 + theories/Automation/Framework.v | 1 - theories/Automation/Hints.v | 157 +++-- theories/Chains/Manipulation.v | 8 +- theories/Libs/Analysis.v | 2 +- theories/Libs/Analysis/ContinuityDomainNat.v | 219 +++++-- theories/Libs/Analysis/ContinuityDomainR.v | 115 +++- theories/Libs/Analysis/LimsupLiminfBolzano.v | 100 ++- theories/Libs/Analysis/MetricSpaces.v | 19 +- theories/Libs/Analysis/OpenAndClosed.v | 64 +- theories/Libs/Analysis/Sequences.v | 217 +++---- theories/Libs/Analysis/SequencesMetric.v | 17 +- .../Analysis/SequentialAccumulationPoints.v | 26 +- theories/Libs/Analysis/Series.v | 2 - .../Analysis/StrongInductionIndexSequence.v | 328 ++++++++++ theories/Libs/Analysis/Subsequences.v | 79 ++- .../Libs/Analysis/SubsequencesInduction.v | 572 ------------------ theories/Libs/Analysis/SubsequencesMetric.v | 58 +- theories/Libs/Analysis/SupAndInf.v | 255 ++++---- theories/Libs/Logic/ConstructiveLogic.v | 130 ++++ theories/Libs/Logic/InformativeEpsilon.v | 45 ++ theories/Libs/Negation.v | 15 +- theories/Libs/Reals.v | 15 +- theories/Notations/Common.v | 4 +- theories/Tactics.v | 1 + theories/Tactics/Assume.v | 43 +- theories/Tactics/Because.v | 36 +- theories/Tactics/BothDirections.v | 8 +- theories/Tactics/BothStatements.v | 32 +- theories/Tactics/Choose.v | 95 +-- theories/Tactics/Claims.v | 2 +- theories/Tactics/Conclusion.v | 185 +++--- theories/Tactics/Contradiction.v | 71 ++- theories/Tactics/Define.v | 2 +- theories/Tactics/Either.v | 134 +++- theories/Tactics/Help.v | 92 +-- theories/Tactics/Induction.v | 33 +- theories/Tactics/ItHolds.v | 146 +++-- theories/Tactics/ItSuffices.v | 113 ++-- theories/Tactics/Obtain.v | 139 +++++ theories/Tactics/Take.v | 35 +- theories/Tactics/ToShow.v | 78 +-- theories/Tactics/Unfold.v | 205 ++++--- theories/Util/Goals.v | 73 +-- theories/Util/MessagesToUser.v | 26 + theories/Util/Since.v | 119 ++++ theories/Version.v | 21 + theories/Waterprove.v | 195 +++++- theories/dune | 11 +- 92 files changed, 3675 insertions(+), 2060 deletions(-) create mode 100644 CHANGES.md rename LICENCE => LICENSE (100%) create mode 100644 tests/automation/Chains.v create mode 100644 tests/dune create mode 100644 tests/libs/Analysis/StrongInductionIndexSequence.v rename tests/tactics/{ChooseSuchThat.v => Obtain.v} (67%) create mode 100644 tests/test-folder.py create mode 100644 tests/util/MessagesToUser.v create mode 100644 theories/Libs/Analysis/StrongInductionIndexSequence.v delete mode 100644 theories/Libs/Analysis/SubsequencesInduction.v create mode 100644 theories/Libs/Logic/ConstructiveLogic.v create mode 100644 theories/Libs/Logic/InformativeEpsilon.v create mode 100644 theories/Tactics/Obtain.v create mode 100644 theories/Util/MessagesToUser.v create mode 100644 theories/Util/Since.v create mode 100644 theories/Version.v diff --git a/.gitignore b/.gitignore index d1bd4821..9a8c6e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,6 @@ META *.aux *.cache *.html -*.css \ No newline at end of file +*.css +*.diags +*.install diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 00000000..5f45f010 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,43 @@ +# Change log for the coq-waterproof library + +## Version 2.1.0+8.17 + +- Improve the `Either` tactic: now proves and destructs ordinary 'ors' when the goal is a proposition +- Improve some mathematical definitions +- Add vernacular for debugging automation + +## Version 2.0.2+8.17 + +- Improve errors and warnings +- Rework expanding definitions +- Deal better with Rabs, Rmax, Rmin + +## Version 2.0.1+8.17 + +- Build the plugin with dune + +## Version 2.0.0 + +- Converted coq library to coq plugin +- Automation procedures are now handled internally in the plugin, so that they can be customized and so that one can check the use of certain lemmas within the automation + +## Version 1.2.4 + +- Added and updated theory files. +- Improved notation for (in)equality chains, e.g. (& a < b <= c = d). +- Bug fixes. + +## Version 1.1.2 + +- Added and updated theory files. +- Reorganized automation libraries. +- Added goal wrappers that force user to write sentences that make proofscript more readable. +- Support chains of (in)equalities for `=`, `<` and `<=` in naturals and reals. +- Fixed issues with several tactics, including `Take ...`. +- Rephrased multiple tactics like `Obtain ... according to ..., ...`. +- For propositions, have user specify types rather than labels in tactics. Labeling is now optional. +- Added some shielding for use of automation, e.g. statements starting with quantifiers cannot be proved automatically. + +## Version 1.0.0 + +- Ported the original library written in ltac to Ltac2. diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/README.md b/README.md index e561a45a..e52dac3d 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,16 @@ The behavior of the automation tactics can be configured by importing specific f Waterproof Set Shorten Databases Foo core. ``` +* **Turn debugging of automation on**: Example: + ```coq + Waterproof Enable Debug Automation. + ``` + +* **Turn debugging of automation off**: Example: + ```coq + Waterproof Disable Debug Automation. + ``` + ## Chains of (in)equalities In written proofs, one often uses a chain of (in)equalities to explain why more complicated (in)equalities hold. Waterproof allows you to use a similar notation in Coq. @@ -183,4 +193,4 @@ The chain of inequalities is used to show that `-r < x - 1`. The coq-waterproof library is developed as part of the educational [Waterproof](https://github.com/impermeable/waterproof) editor for Coq. The tactics are designed to be used by first-year mathematics students who are unfamiliar with Coq. This is also why the tactics require the user to be explicit: the students have to learn to write readable proofs. -The library was originally written by Jim Portegies in Ltac1. It was extended and ported to Ltac2 by Cosmin Manea, Lulof Pirée, Adrian Vrămuleţ and Tudor Voicu as part of the 'Waterfowl' bachelor Software Engineering Project at the [Eindhoven University of Technology](https://www.tue.nl/en/) (in May-June 2021). Since then it has been under further development by Jelle Wemmenhove and Jim Portegies. +The library was originally written by Jim Portegies in Ltac1. It was extended and ported to Ltac2 by Cosmin Manea, Lulof Pirée, Adrian Vrămuleţ and Tudor Voicu as part of the 'Waterfowl' bachelor Software Engineering Project at the [Eindhoven University of Technology](https://www.tue.nl/en/) (in May-June 2021). Since then it has been under further development by Jelle Wemmenhove and Jim Portegies. In April-June 2023, Balthazar Patiachvili improved the automation, and converted parts of the library to an OCaml plugin. diff --git a/_CoqProject.in b/_CoqProject.in index a4cfc2f4..3afe4dfa 100644 --- a/_CoqProject.in +++ b/_CoqProject.in @@ -1,5 +1,10 @@ src/META.coq-waterproof -R theories/ Waterproof +# The following three lines can be commented out +# to make coq-lsp work while working on the library: +# -R _build/default/theories/ Waterproof +# -I _build/install/default/lib/coq-waterproof/plugin +# -I _build/install/default/lib/ -I src -I @LTAC2_CAML_FILES@ @@ -32,6 +37,7 @@ src/waterproof.mlpack theories/Automation.v theories/Automation/Framework.v theories/Automation/Hints.v +theories/Version.v theories/Chains.v theories/Chains/Manipulation.v @@ -52,6 +58,7 @@ theories/Tactics/BothDirections.v theories/Tactics/BothStatements.v theories/Tactics/Claims.v theories/Tactics/Choose.v +theories/Tactics/Obtain.v theories/Tactics/Conclusion.v theories/Tactics/Contradiction.v theories/Tactics/Define.v @@ -69,6 +76,8 @@ theories/Util/Constr.v theories/Util/Goals.v theories/Util/Hypothesis.v theories/Util/Init.v +theories/Util/Since.v +theories/Util/MessagesToUser.v theories/Waterproof.v theories/Waterprove.v @@ -76,6 +85,8 @@ theories/Waterprove.v # Coq-Waterproof libraries ## Analysis +theories/Libs/Logic/InformativeEpsilon.v +theories/Libs/Logic/ConstructiveLogic.v theories/Libs/Analysis.v theories/Libs/Analysis/ContinuityDomainNat.v theories/Libs/Analysis/ContinuityDomainR.v @@ -86,8 +97,8 @@ theories/Libs/Analysis/SequencesMetric.v theories/Libs/Analysis/Sequences.v theories/Libs/Analysis/SequentialAccumulationPoints.v theories/Libs/Analysis/Series.v -theories/Libs/Analysis/SubsequencesInduction.v theories/Libs/Analysis/SubsequencesMetric.v +theories/Libs/Analysis/StrongInductionIndexSequence.v theories/Libs/Analysis/Subsequences.v theories/Libs/Analysis/SupAndInf.v @@ -101,6 +112,7 @@ tests/chains/Manipulation.v ### General tests/automation/Shield.v +tests/automation/Chains.v ### Databases tests/automation/databases/Core.v @@ -116,6 +128,7 @@ tests/tactics/BothDirections.v tests/tactics/BothStatements.v tests/tactics/Claims.v tests/tactics/Choose.v +tests/tactics/Obtain.v tests/tactics/Conclusion.v tests/tactics/Contradiction.v tests/tactics/Define.v @@ -126,6 +139,8 @@ tests/tactics/ItSuffices.v tests/tactics/Take.v tests/tactics/ToShow.v tests/tactics/Unfold.v +tests/util/MessagesToUser.v ## Libs -tests/libs/Negation.v \ No newline at end of file +tests/libs/Negation.v +tests/libs/Analysis/StrongInductionIndexSequence.v diff --git a/coq-waterproof.opam b/coq-waterproof.opam index 7f08bb88..c40b2e0b 100644 --- a/coq-waterproof.opam +++ b/coq-waterproof.opam @@ -1,8 +1,16 @@ opam-version: "2.0" name: "coq-waterproof" -version: "2.0.0" +version: "2.1.0+8.19" maintainer: "Jim Portegies " -authors: [ "Jim Portegies " ] +authors: [ + "Jelle Wemmenhove" + "Balthazar Pathiachvili" + "Cosmin Manea" + "Lulof Pirée" + "Adrian Vrămuleţ" + "Tudor Voicu" + "Jim Portegies " +] synopsis: "Coq proofs in a style that resembles non-mechanized mathematical proofs" description: """ @@ -12,17 +20,28 @@ Mathematicians unfamiliar with the Coq syntax are able to read the resulting pro license: "LGPL-3.0-or-later" homepage: "https://github.com/impermeable/coq-waterproof" +dev-repo: "git+https://github.com/impermeable/coq-waterproof.git" bug-reports: "https://github.com/impermeable/coq-waterproof/issues" -depends: [ - "ocaml" {>= "4.14.1"} - "coq" {>= "8.17"} +depends: [ + "ocaml" {>= "4.09.0"} + "coq" {>= "8.19" & < "8.20" | = "dev"} + "dune" {>= "3.6."} ] build: [ - ["dune" "build" "-p" "coq-waterproof"] + ["dune" "build" "-p" "coq-waterproof" "@install"] ] install: [ ["dune" "install" "-p" "coq-waterproof"] ] + +available: arch != "s390x" + +tags: [ + "keyword:mathematics education" + "category:Mathematics/Education" + "date:2023-11-04" + "logpath:Waterproof" +] diff --git a/dune-project b/dune-project index 2a0bccc2..4f464b0a 100644 --- a/dune-project +++ b/dune-project @@ -3,5 +3,6 @@ (package (name coq-waterproof) + (version 2.1.0+8.19) (synopsis "Coq proofs in a style that resembles non-mechanized mathematical proofs") - (depends coq-core coq-stdlib)) \ No newline at end of file + (depends coq-core coq-stdlib)) diff --git a/src/META.coq-waterproof b/src/META.coq-waterproof index c00ffd84..e72c9005 100644 --- a/src/META.coq-waterproof +++ b/src/META.coq-waterproof @@ -1,6 +1,6 @@ package "waterproof" ( directory = "." - version = "dev" + version = "2.1.0+8.19" description = "Waterproof plugin" requires = "coq-core.plugins.ltac coq-core.plugins.ltac2" archive(byte) = "waterproof.cma" diff --git a/src/exceptions.ml b/src/exceptions.ml index 006a3c3e..7b8b541d 100644 --- a/src/exceptions.ml +++ b/src/exceptions.ml @@ -32,6 +32,7 @@ type wexn = | FailedTest of string (** Indicates that the running test has failed *) | NonExistingDataset of Hints.hint_db_name (** Indicates that the user tried to import a non-existing hint dataset *) | UnusedLemmas (** Indicates that no proof using all the given lemmas has been found *) + | ToUserError of Pp.t (** An error that should go directly to the user *) (** Converts errors to displayable messages @@ -43,10 +44,23 @@ let pr_wexn (exn: wexn): t = | FailedTest test -> str "Failed test: " ++ str test | NonExistingDataset dataset -> str "Non existing dataset: the dataset " ++ str dataset ++ str " is not defined" | UnusedLemmas -> str "No proof using all given lemmas has been found" + | ToUserError message -> message (** Throws an error with given info and message *) let throw ?(info: Exninfo.info = Exninfo.null) (exn: wexn): 'a = let fatal = Exninfo.add info fatal_flag () in - CErrors.user_err ?info:(Some fatal) (pr_wexn exn) \ No newline at end of file + CErrors.user_err ?info:(Some fatal) (pr_wexn exn) + +(** + Sends a warning and returns the message as a string +*) +let warn (input : Pp.t) : unit Proofview.tactic = + Proofview.tclUNIT @@ Feedback.msg_warning input + +(** + Throws an error +*) +let err (input : Pp.t) : unit Proofview.tactic = + Proofview.tclUNIT @@ throw (ToUserError input) diff --git a/src/exceptions.mli b/src/exceptions.mli index 57fa7a70..f7d7d0e2 100644 --- a/src/exceptions.mli +++ b/src/exceptions.mli @@ -25,8 +25,21 @@ type wexn = | FailedTest of string (** Indicates that the running test has failed *) | NonExistingDataset of Hints.hint_db_name (** Indicates that the user tried to import a non-existing hint dataset *) | UnusedLemmas (** Indicates that no proof using all the given lemmas has been found *) + | ToUserError of Pp.t (** An error that should go directly to the user *) (** Throws an error with given info and message *) val throw : ?info:Exninfo.info -> wexn -> 'a + +(** + Sends a warning +*) +val warn : + Pp.t -> unit Proofview.tactic + +(** + Throws an error +*) +val err : + Pp.t -> unit Proofview.tactic diff --git a/src/g_waterproof.mlg b/src/g_waterproof.mlg index 934df556..ede7e46e 100644 --- a/src/g_waterproof.mlg +++ b/src/g_waterproof.mlg @@ -33,6 +33,7 @@ open Exceptions open Hint_dataset_declarations open Waterprove +let waterproof_version : string = "2.1.0+8.19" } VERNAC COMMAND EXTEND AutomationShieldEnableSideEff CLASSIFIED AS SIDEFF @@ -49,6 +50,41 @@ VERNAC COMMAND EXTEND AutomationShieldDisableSideEff CLASSIFIED AS SIDEFF } END +VERNAC COMMAND EXTEND AutomationDebugEnableSideEff CLASSIFIED AS SIDEFF + | [ "Waterproof" "Enable" "Debug" "Automation" ] -> + { + automation_debug := true + } +END + +VERNAC COMMAND EXTEND AutomationDebugDisableSideEff CLASSIFIED AS SIDEFF + | [ "Waterproof" "Disable" "Debug" "Automation" ] -> + { + automation_debug := false + } +END + +VERNAC COMMAND EXTEND AutomationPrintRewriteHintsEnableSideEff CLASSIFIED AS SIDEFF + | [ "Waterproof" "Enable" "Printing" "Rewrite" "Hints"] -> + { + print_rewrite_hints := true + } +END + +VERNAC COMMAND EXTEND AutomationPrintRewriteHintsDisableSideEff CLASSIFIED AS SIDEFF + | [ "Waterproof" "Disable" "Printing" "Rewrite" "Hints" ] -> + { + print_rewrite_hints := false + } +END + +VERNAC COMMAND EXTEND PrintVersionSideEff CLASSIFIED AS SIDEFF + | [ "Waterproof" "Print" "Version" ] -> + { + Feedback.msg_notice (Pp.str waterproof_version) + } +END + { (** Creates a name used to define the function interface *) @@ -192,4 +228,13 @@ let () = forbidden end >>= fun () -> tclUNIT @@ of_unit () +let () = + define1 "warn_external" pp @@ + fun input -> + warn input >>= fun () -> tclUNIT @@ of_unit () + +let () = + define1 "throw_external" pp @@ + fun input -> + err input >>= fun () -> tclUNIT @@ of_unit () } diff --git a/src/hint_dataset.ml b/src/hint_dataset.ml index 733a3632..f6a8b58d 100644 --- a/src/hint_dataset.ml +++ b/src/hint_dataset.ml @@ -32,7 +32,7 @@ let loaded_hint_dataset: string list ref = ref ~name:"loaded_hint_dataset" [] Dictionary with dataset names as keys and datasets as values *) let existing_datasets: hint_dataset StringMap.t ref = - ref ~name:"existing_datasets" @@ List.fold_left (fun dict (name, dataset) -> StringMap.add name dataset dict) StringMap.empty [("Empty", empty); ("Core", core); ("Algebra", algebra); ("Integers", integers); ("RealsAndIntegers", reals_and_integers); ("Sets", sets); ("Intuition", intuition)] + ref ~name:"existing_datasets" @@ List.fold_left (fun dict (name, dataset) -> StringMap.add name dataset dict) StringMap.empty [("Empty", empty); ("Core", core); ("Algebra", algebra); ("Integers", integers); ("RealsAndIntegers", reals_and_integers); ("Sets", sets); ("Intuition", intuition); ("ClassicalEpsilon", classical_epsilon)] (** Adds a dataset to the currently loaded hint datasets diff --git a/src/hint_dataset_declarations.ml b/src/hint_dataset_declarations.ml index c5eaeed2..421494c2 100644 --- a/src/hint_dataset_declarations.ml +++ b/src/hint_dataset_declarations.ml @@ -86,41 +86,48 @@ let empty: hint_dataset = { let core: hint_dataset = { name = "Core"; main_databases: hint_db_name list = ["core"]; - decidability_databases: hint_db_name list = ["core"]; + decidability_databases: hint_db_name list = ["nocore"]; shorten_databases: hint_db_name list = ["core"]; } let algebra: hint_dataset = { name = "Algebra"; - main_databases: hint_db_name list = ["wp_core"; "arith"; "wp_algebra"; "wp_classical_logic"; "wp_constructive_logic"; "wp_integers"; "zarith"; "wp_negation_nat"; "wp_negation_int"]; + main_databases: hint_db_name list = ["wp_core"; "arith"; "zarith"; "wp_algebra"; "wp_integers"; "wp_negation_int"]; decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_classical"; "wp_decidability_nat"]; - shorten_databases: hint_db_name list = ["wp_classical_logic"; "wp_constructive_logic"; "wp_core"]; + shorten_databases: hint_db_name list = ["wp_core"; "wp_negation_int"]; } let integers: hint_dataset = { name = "Integers"; - main_databases: hint_db_name list = ["arith"; "zarith"; "wp_core"; "wp_classical_logic"; "wp_constructive_logic"; "wp_integers"; "wp_negation_nat"; "wp_negation_int"]; - decidability_databases: hint_db_name list = ["wp_decidability_nat"]; - shorten_databases: hint_db_name list = ["wp_classical_logic"]; + main_databases: hint_db_name list = ["arith"; "zarith"; "wp_core"; "wp_integers"; "wp_negation_int"]; + decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_nat"]; + shorten_databases: hint_db_name list = ["wp_core"; "wp_negation_int"]; } let reals_and_integers: hint_dataset = { name = "RealsAndIntegers"; - main_databases: hint_db_name list = ["arith"; "zarith"; "real"; "wp_core"; "wp_classical_logic"; "wp_constructive_logic"; "wp_integers"; "wp_reals"; "wp_sets"; "wp_negation_nat"; "wp_negation_int"; "wp_negation_reals"; "wp_negation_nat"; "wp_negation_int"; "wp_negation_reals"]; - decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_nat"; "wp_decidability_reals"; "wp_core"; "wp_reals"]; - shorten_databases: hint_db_name list = ["wp_sets"; "wp_classical_logic"]; + main_databases: hint_db_name list = ["arith"; "zarith"; "real"; "wp_core"; "wp_definitions"; "wp_integers"; "wp_reals"; "wp_negation_reals"]; + decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_nat"; "wp_decidability_reals"; "wp_decidability_classical"; "wp_decidability_constructive"]; + shorten_databases: hint_db_name list = ["wp_core"; "wp_definitions"; "wp_negation_reals"]; } let sets: hint_dataset = { name = "Sets"; - main_databases: hint_db_name list = ["arith"; "zarith"; "wp_core"; "wp_classical_logic"; "wp_constructive_logic"; "wp_integers"; "wp_negation_nat"; "wp_negation_int"]; - decidability_databases: hint_db_name list = ["wp_decidability_nat"]; - shorten_databases: hint_db_name list = ["wp_classical_logic"]; + main_databases: hint_db_name list = ["arith"; "zarith"; "wp_core"; "wp_integers"; "wp_negation_int"]; + decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_nat"]; + shorten_databases: hint_db_name list = ["wp_core"; "wp_negation_int"]; } let intuition: hint_dataset = { name = "Intuition"; main_databases: hint_db_name list = ["wp_intuition"]; decidability_databases: hint_db_name list = []; + shorten_databases: hint_db_name list = ["wp_intuition"]; +} + +let classical_epsilon: hint_dataset = { + name = "ClassicalEpsilon"; + main_databases: hint_db_name list = []; + decidability_databases: hint_db_name list = ["nocore"; "wp_decidability_epsilon"]; shorten_databases: hint_db_name list = []; -} \ No newline at end of file +} diff --git a/src/hint_dataset_declarations.mli b/src/hint_dataset_declarations.mli index 6c2e368a..817e5f76 100644 --- a/src/hint_dataset_declarations.mli +++ b/src/hint_dataset_declarations.mli @@ -64,4 +64,5 @@ val algebra : hint_dataset val integers : hint_dataset val reals_and_integers : hint_dataset val sets : hint_dataset -val intuition: hint_dataset \ No newline at end of file +val intuition: hint_dataset +val classical_epsilon : hint_dataset diff --git a/src/proofutils.mli b/src/proofutils.mli index 5c2a1b95..4226b601 100644 --- a/src/proofutils.mli +++ b/src/proofutils.mli @@ -62,7 +62,6 @@ module StringMap : sig val for_all : (key -> 'a -> bool) -> 'a t -> bool val exists : (key -> 'a -> bool) -> 'a t -> bool val filter : (key -> 'a -> bool) -> 'a t -> 'a t - val filter_map : (key -> 'a -> 'b option) -> 'a t -> 'b t val partition : (key -> 'a -> bool) -> 'a t -> 'a t * 'a t val cardinal : 'a t -> int val bindings : 'a t -> (key * 'a) list @@ -88,7 +87,6 @@ module StringMap : sig val map : ('a -> 'b) -> 'a t -> 'b t val mapi : (key -> 'a -> 'b) -> 'a t -> 'b t val to_seq : 'a t -> (key * 'a) Seq.t - val to_rev_seq : 'a t -> (key * 'a) Seq.t val to_seq_from : key -> 'a t -> (key * 'a) Seq.t val add_seq : (key * 'a) Seq.t -> 'a t -> 'a t val of_seq : (key * 'a) Seq.t -> 'a t diff --git a/src/waterprove.ml b/src/waterprove.ml index b1b8ded2..a63a3116 100644 --- a/src/waterprove.ml +++ b/src/waterprove.ml @@ -20,7 +20,6 @@ open EConstr open Hints open Proofview -open Exceptions open Hint_dataset open Hint_dataset_declarations open Wp_auto @@ -32,15 +31,25 @@ open Wp_rewrite *) let automation_shield: bool ref = Summary.ref ~name:"automation_shield" true +(** + Do we want to debug the automation ? +*) +let automation_debug : bool ref = Summary.ref ~name:"automation_debug" false + +(** + Should rewrite hints be printed ? +*) +let print_rewrite_hints : bool ref = Summary.ref ~name:"print_rewrite_hints" false + (** Function that will actually call automation functions *) let automation_routine (depth: int) (lems: Tactypes.delayed_open_constr list) (databases: hint_db_name list): unit tactic = Tacticals.tclFIRST [ - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_auto false depth lems databases; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_eauto false depth lems databases; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite @@ wp_auto false depth lems databases; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite @@ wp_eauto false depth lems databases + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_auto !automation_debug depth lems databases; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_eauto !automation_debug depth lems databases; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite ~print_hints:(!print_rewrite_hints) !automation_debug @@ wp_auto !automation_debug depth lems databases; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite ~print_hints:(!print_rewrite_hints) !automation_debug @@ wp_eauto !automation_debug depth lems databases ] (** @@ -48,10 +57,10 @@ let automation_routine (depth: int) (lems: Tactypes.delayed_open_constr list) (d *) let restricted_automation_routine (depth: int) (lems: Tactypes.delayed_open_constr list) (databases: hint_db_name list) (must_use: Pp.t list) (forbidden: Pp.t list): unit tactic = Tacticals.tclFIRST [ - Tacticals.tclCOMPLETE @@ tclIGNORE @@ rwp_auto false depth lems databases must_use forbidden; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ rwp_eauto false depth lems databases must_use forbidden; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite @@ wp_auto false depth lems databases; - Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite @@ wp_eauto false depth lems databases + Tacticals.tclCOMPLETE @@ tclIGNORE @@ rwp_auto !automation_debug depth lems databases must_use forbidden; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ rwp_eauto !automation_debug depth lems databases must_use forbidden; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite ~print_hints:(!print_rewrite_hints) !automation_debug @@ wp_auto !automation_debug depth lems databases; + Tacticals.tclCOMPLETE @@ tclIGNORE @@ wp_autorewrite ~print_hints:(!print_rewrite_hints) !automation_debug @@ wp_eauto !automation_debug depth lems databases ] (** @@ -72,12 +81,10 @@ let restricted_automation_routine (depth: int) (lems: Tactypes.delayed_open_cons let waterprove (depth: int) ?(shield: bool = false) (lems: Tactypes.delayed_open_constr list) (database_type: database_type): unit tactic = Proofview.Goal.enter @@ fun goal -> begin - tclORELSE - (automation_routine 2 lems (get_current_databases database_type)) - begin fun _ -> - if shield && !automation_shield then throw (FailedAutomation "The current goal cannot be proved since it contains shielded patterns"); - automation_routine depth lems (get_current_databases database_type) - end + if shield && !automation_shield then + automation_routine 3 lems (get_current_databases Shorten) + else + automation_routine depth lems (get_current_databases database_type) end (** @@ -100,10 +107,8 @@ let rwaterprove (depth: int) ?(shield: bool = false) (lems: Tactypes.delayed_ope let sigma = Proofview.Goal.sigma goal in let must_use_tactics = List.map (Printer.pr_econstr_env env sigma) must_use in let forbidden_tactics = List.map (Printer.pr_econstr_env env sigma) forbidden in - tclORELSE - (restricted_automation_routine 2 lems (get_current_databases database_type) must_use_tactics forbidden_tactics) - begin fun _ -> - if shield && !automation_shield then throw (FailedAutomation "The current goal cannot be proved since it contains shielded patterns"); - restricted_automation_routine depth lems (get_current_databases database_type) must_use_tactics forbidden_tactics - end - end \ No newline at end of file + if shield && !automation_shield then + restricted_automation_routine 3 lems (get_current_databases Shorten) must_use_tactics forbidden_tactics + else + restricted_automation_routine depth lems (get_current_databases database_type) must_use_tactics forbidden_tactics + end diff --git a/src/waterprove.mli b/src/waterprove.mli index 00f9a87f..02df44f4 100644 --- a/src/waterprove.mli +++ b/src/waterprove.mli @@ -21,6 +21,16 @@ *) val automation_shield : bool ref +(** + Do we want to debug the automation ? +*) +val automation_debug : bool ref + +(** + Should rewrite hints be printed ? +*) +val print_rewrite_hints: bool ref + (** Waterprove diff --git a/src/wp_rewrite.ml b/src/wp_rewrite.ml index 7d058dda..b748225d 100644 --- a/src/wp_rewrite.ml +++ b/src/wp_rewrite.ml @@ -347,8 +347,7 @@ let find_applied_relation ?(loc: Loc.t option) (env: Environ.env) sigma c left2r ) let fill_rewrite_tab (env: Environ.env) (sigma: Evd.evar_map) (rule : raw_rew_rule) (rewrite_database: rewrite_db): rewrite_db = - let env = Global.env () in - let ist = Genintern.empty_glob_sign ~strict:true (Global.env ()) in + let ist = Genintern.empty_glob_sign ~strict:true env in let intern (tac: raw_generic_argument): glob_generic_argument = snd (Genintern.generic_intern ist tac) in let to_rew_rule ({CAst.loc;v=((c,ctx),b,t)}: raw_rew_rule): rew_rule = let sigma = Evd.merge_context_set Evd.univ_rigid sigma ctx in @@ -411,12 +410,13 @@ let fill_local_rewrite_database (): rewrite_db tactic = This tactic is a rewrite of the coq-core's [autorewrite] tactic that will only consider current hypothesis as rewrite hints. *) -let wp_autorewrite ?(print_hints: bool = false) (tac: trace tactic): unit tactic = +let wp_autorewrite ?(print_hints: bool = false) (log: bool) (tac: trace tactic): unit tactic = let clause = {onhyps = Some []; concl_occs = Locus.AllOccurrences} in fill_local_rewrite_database () >>= fun rewrite_db -> Goal.enter @@ begin fun goal -> let env = Goal.env goal in let sigma = Goal.sigma goal in if print_hints then Feedback.msg_notice @@ print_rewrite_hintdb env sigma rewrite_db; + if log then Feedback.msg_notice @@ str "(* application of wp_autorewrite *)"; Tacticals.tclREPEAT @@ tclPROGRESS @@ gen_auto_multi_rewrite tac clause rewrite_db end >>= fun _ -> tclUNIT () diff --git a/src/wp_rewrite.mli b/src/wp_rewrite.mli index ba99c2e8..dc81f99b 100644 --- a/src/wp_rewrite.mli +++ b/src/wp_rewrite.mli @@ -23,5 +23,6 @@ *) val wp_autorewrite : ?print_hints:bool -> + bool -> Backtracking.trace Proofview.tactic -> unit Proofview.tactic diff --git a/tests/automation/Chains.v b/tests/automation/Chains.v new file mode 100644 index 00000000..dd312ad7 --- /dev/null +++ b/tests/automation/Chains.v @@ -0,0 +1,82 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. + +Require Import Waterproof.Waterproof. +Require Import Waterproof.Notations. + +Require Import Waterproof.Waterprove. + + +(** Tests whether the error points out which specific (in)equality in the chain does not hold. *) +Local Parameter X : Type. +Local Parameter a b c : X. + +(* Test 1: first equality does not hold. *) +Goal (& a = b = c). +Proof. + Fail waterprove 5 true Main. (* Expected: unable to find proof (a = b) *) +Abort. + +(* Test 2: last equality does not hold. *) +Goal (a = b) -> (& a = b = c). +Proof. + intro p. + Fail waterprove 5 true Main. (* Expected: unable to find proof (b = c) *) +Abort. + +(** Test restricted automation. *) +Variable P : Prop. +Variable h : P -> a = b. +#[local] Hint Extern 1 => symmetry : core. + +(* Test 3: fails without extra lemma. *) +Goal P -> (& a = b = b = b = b = a). +Proof. + intro H. + Fail waterprove 5 true Main. +Abort. + +(* Test 4: extra lemma has to be used in first equality. *) +Goal P -> (& a = b = b = b = b = b). +Proof. + intro H. + rwaterprove 5 true Main constr:(h). +Abort. + +(* Test 5: extra lemma has to be used in last equality. *) +Goal P -> (& b = b = b = b = b = a). +Proof. + intro H. + rwaterprove 5 true Main constr:(h). +Abort. + +(* Test 6: extra lemma has to be used in 2nd and 2nd-to-last equality. *) +Goal P -> (& b = b = a = b = b = a = a). +Proof. + intro H. + rwaterprove 5 true Main constr:(h). +Abort. + +(* Test 7: Fails if extra lemma is never used. *) +Goal P -> (P -> b = c) -> (& b = b = c = b = b = c = c). +Proof. + intros H1 H2. + Fail rwaterprove 5 true Main constr:(h). +Abort. \ No newline at end of file diff --git a/tests/automation/Shield.v b/tests/automation/Shield.v index 7237478e..327e0cbe 100644 --- a/tests/automation/Shield.v +++ b/tests/automation/Shield.v @@ -126,7 +126,7 @@ Abort. (** Testing de Morgan laws. *) Require Import Waterproof.Libs.Negation. -#[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation ()) : wp_classical_logic. +#[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation (fun () => ())) : wp_negation_logic. (** Level 1 *) Local Parameter P1 : R -> Prop. diff --git a/tests/automation/databases/Core.v b/tests/automation/databases/Core.v index 9ced7980..d90a95ea 100644 --- a/tests/automation/databases/Core.v +++ b/tests/automation/databases/Core.v @@ -19,15 +19,25 @@ Require Import Waterproof.Waterproof. Require Import Waterproof.Automation. Require Import Waterproof.Waterprove. +Require Import Ltac2.Ltac2. Waterproof Enable Automation Core. Goal forall x: nat, 0 = 0. Proof. - waterprove 5 false [] Main. + waterprove 5 false Main. Qed. Goal forall x y: nat, forall f: nat -> nat, x = y -> f (S x) = f (S y). Proof. - waterprove 5 false [] Main. -Qed. \ No newline at end of file + waterprove 5 false Main. +Qed. + +(** + * Test of wp_autorewrite +*) +Goal forall P : nat -> Prop, forall p q : nat, p = q -> P q -> P p. +Proof. + intros. + waterprove 5 false Main. +Qed. diff --git a/tests/automation/databases/Decidability.v b/tests/automation/databases/Decidability.v index 16326f10..7f96417b 100644 --- a/tests/automation/databases/Decidability.v +++ b/tests/automation/databases/Decidability.v @@ -20,6 +20,62 @@ Require Import Waterproof.Waterproof. Require Import Waterproof.Automation. Goal forall P : Prop, {P} + {~P}. +Proof. + auto with wp_decidability_epsilon. +Qed. + +Goal forall P Q : Prop, P \/ Q -> {P} + {Q}. +Proof. + auto with wp_decidability_epsilon. +Qed. + +Goal forall P : Prop, P \/ ~P. Proof. auto with wp_decidability_classical. -Qed. \ No newline at end of file +Qed. + +Goal forall P Q : Prop, {P} + {Q} -> P \/ Q. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q : Prop, {Q} + {P} -> Q \/ P. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {P} + {Q} + {R} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {P} + {R} + {Q} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {Q} + {P} + {R} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {Q} + {R} + {P} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {R} + {P} + {Q} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall P Q R: Prop, {R} + {P} + {Q} -> P \/ Q \/ R. + auto with wp_decidability_constructive. +Qed. + +Goal forall n : nat, n < 5 \/ n > 4. +Proof. + intro n. + auto with wp_decidability_nat. +Qed. + +Require Import Reals.Reals. +Open Scope R_scope. +Goal forall x : R, x < 5 \/ x > 4. +Proof. + intro x. + auto with wp_decidability_reals. +Qed. diff --git a/tests/automation/databases/Empty.v b/tests/automation/databases/Empty.v index da15f6e4..cb93c48e 100644 --- a/tests/automation/databases/Empty.v +++ b/tests/automation/databases/Empty.v @@ -19,6 +19,7 @@ Require Import Waterproof.Waterproof. Require Import Waterproof.Automation. Require Import Waterproof.Waterprove. +Require Import Ltac2.Ltac2. Waterproof Clear Automation. diff --git a/tests/automation/databases/Integers.v b/tests/automation/databases/Integers.v index 5d332f71..22a742de 100644 --- a/tests/automation/databases/Integers.v +++ b/tests/automation/databases/Integers.v @@ -26,31 +26,33 @@ Require Import Waterproof.Tactics. Open Scope nat_scope. (* Test 0: check if notations work. *) +Require Import Lia. + Goal ∀ n : ℕ -> ℕ, (∀ k : ℕ, (n (k + 1) > n k)%nat) ⇒ ∀ k : ℕ, (n k ≥ k)%nat. -intro n. -intro H. -induction k as [| k IHk]. -- solve [auto with wp_integers]. -- assert (H1 : S k = k + 1) by (auto with wp_integers). - rewrite H1. - assert (H2 : n (k + 1) > n k) by (auto with wp_integers). - auto with wp_integers. + intro n. + intro H. + induction k as [| k IHk]. + - solve [auto with wp_integers zarith]. + - assert (H1 : S k = k + 1) by (auto with wp_integers zarith). + rewrite H1. + assert (H2 : n (k + 1) > n k) by (auto with wp_integers zarith). + auto with wp_integers zarith. Qed. Require Import Lia. Goal (& 3 < 4 <= 5). -solve [auto with wp_core wp_integers]. + cbn; repeat split; solve [ltac1:(auto with wp_core wp_integers)]. Qed. Goal (& 3 = 3 = 3). -solve [auto with wp_core wp_integers]. + cbn; repeat split; solve [auto with wp_core wp_integers]. Qed. (* Test 1: check if terms of a subset can be coerced to terms of the underlying set (here: [R]). *) Goal forall x : nat, (& x < 5 = 2 + 3) -> (x < 5). -intro x. -intro H. -simpl_ineq_chains (). -solve [auto with wp_integers]. + intro x. + intro H. + simpl_ineq_chains (). + solve [auto with wp_integers zarith]. Qed. \ No newline at end of file diff --git a/tests/automation/databases/RealsAndIntegers.v b/tests/automation/databases/RealsAndIntegers.v index 19eb6197..77a0def8 100644 --- a/tests/automation/databases/RealsAndIntegers.v +++ b/tests/automation/databases/RealsAndIntegers.v @@ -17,6 +17,7 @@ (******************************************************************************) Require Import Coq.Reals.Reals. +Require Import Ltac2.Ltac2. Require Import Waterproof.Waterproof. Require Import Waterproof.Automation. @@ -29,26 +30,47 @@ Open Scope R_scope. Goal forall x y: R, forall f: R -> R, x = y -> f (x + 1) = f (y + 1). Proof. - waterprove 5 false [] Main. + waterprove 5 false Main. Qed. Goal forall x y: R, forall f: R -> R, x = y -> f x = f y /\ x = y. Proof. - waterprove 5 false [] Main. + waterprove 5 false Main. Qed. Goal (& 3 < 4 <= 5). - auto with wp_core wp_reals. + cbn; repeat split; auto with wp_core wp_reals. Qed. Goal (& 3 = 3 = 3). - auto with wp_core wp_reals. + cbn; repeat split; auto with wp_core wp_reals. Qed. Goal forall x : R, (& x < 5 = 2 + 3) -> (x < 5). intro x. intro H. + auto with wp_core wp_reals. +Qed. + +(** ** Testcases to deal with Rabs Rmin Rmax *) + +Goal forall b: R, b > 0 -> - Rmax( 0, 1 - b/2) >= - 1. + auto with wp_reals. +Qed. + +Goal forall b: R, b > 0 -> Rmin( 0, -1 + b/2) <= 1. + auto with wp_reals. +Qed. + +Goal forall r : R, r > 0 -> + | Rmax 0 (1 - r/2) - 1 | = 1 - (Rmax 0 (1 - r/2)). + auto with wp_reals. +Qed. + +Goal forall x r : R, r > 0 -> + x = Rmax 0 (1 - r/2) -> Rabs (x - 1) < r. + intros x r r_gt_0 x_eq_Rmax. auto with wp_reals. Qed. -Close Scope R_scope. \ No newline at end of file +Close Scope R_scope. diff --git a/tests/dune b/tests/dune new file mode 100644 index 00000000..4ef84f34 --- /dev/null +++ b/tests/dune @@ -0,0 +1,8 @@ +(coq.theory + (name __WaterproofTests) + (package coq-waterproof) + (flags :standard) + (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2) + (theories Waterproof)) + +(include_subdirs qualified) diff --git a/tests/libs/Analysis/StrongInductionIndexSequence.v b/tests/libs/Analysis/StrongInductionIndexSequence.v new file mode 100644 index 00000000..c6134c19 --- /dev/null +++ b/tests/libs/Analysis/StrongInductionIndexSequence.v @@ -0,0 +1,94 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Waterproof.Libs.Analysis.StrongInductionIndexSequence. + +Variable Q : nat -> Prop. + + +(* Test 1: without other Waterproof tactics. *) +Goal (exists n : nat -> nat, is_index_seq n /\ forall k : nat, Q (n k)). +Proof. + Define the index sequence n inductively. + - pose (n_0 := 0); exists n_0. + admit. + - Take k : ℕ and assume n(0),...,n(k) are defined. + intros H1 H2. + pose (n_kplus1 := 0); exists n_kplus1. + split. + + admit. + + admit. +Abort. + + +(* Test 2: with other Waterproof tactics. *) +Require Import Waterproof.Tactics. +Goal (exists n : nat -> nat, is_index_seq n /\ forall k : nat, Q (n k)). +Proof. + Define the index sequence n inductively. + - Choose n0 := 0. + admit. + - Take k : ℕ and assume n(0),...,n(k) are defined. + Assume that (forall l : nat, l <= k -> Q (n l)). + Assume that (forall l : nat, l < k -> n l < n (l + 1)). + Choose n_kplus1 := 0. + We show both statements. + + We need to show that (Q n_kplus1). + admit. + + We need to show that (n k < n_kplus1). + admit. +Abort. + + + +(* Test 3: more complicated example from actual exercises, + i.e. with function notation 'n(k)' and 'P(n(k)) = blue' as property. *) +Require Import Waterproof.Notations. +Require Import Waterproof.Automation. + +Waterproof Enable Automation RealsAndIntegers. +Waterproof Enable Automation Intuition. + +Inductive Color : Set := +| blue : Color +| orange : Color. + +Variable P : ℕ → Color. +Parameter infinitely_many_blues : ∀ k : ℕ, ∃ m : ℕ, (m ≥ k)%nat ∧ P(m) = blue. + +Lemma exercise_10_7_6 : + ∃ n : ℕ → ℕ, (is_index_seq n) ∧ (∀ k : ℕ, P (n k) = blue). +Proof. +Define the index sequence n inductively. +- By (infinitely_many_blues) it holds that + (∃ m : ℕ, (m ≥ 0)%nat ∧ P(m) = blue). + Obtain such an m. + Choose n_0 := m. + We conclude that (P(n_0) = blue). +- Take k : ℕ and assume n(0),...,n(k) are defined. + Assume that (∀ l : ℕ, (l ≤ k)%nat ⇒ P(n(l)) = blue). + Assume that (∀ l : ℕ, (l < k)%nat ⇒ (n(l) < n(l+1))%nat). + By (infinitely_many_blues) it holds that + (∃ m : ℕ, (m ≥ (n k) + 1)%nat ∧ P(m) = blue). + Obtain such an m. + Choose n_kplus1 := m. + We show both statements. + * We conclude that (P(n_kplus1) = blue). + * We conclude that (n(k) < n_kplus1)%nat. +Qed. \ No newline at end of file diff --git a/tests/libs/Negation.v b/tests/libs/Negation.v index 3f4afd9c..3c444f2d 100644 --- a/tests/libs/Negation.v +++ b/tests/libs/Negation.v @@ -196,7 +196,7 @@ Local Parameter A : R -> Prop. Goal (~ (exists x : R, A x /\ L < x)) -> (forall x : R, A x -> ~(L < x)). Proof. intro H. - solve_by_manipulating_negation (). + solve_by_manipulating_negation (fun () => ()). Qed. @@ -210,7 +210,7 @@ Goal ~ (forall eps : R, eps > 0 -> exists delta : R, delta > 0 -> forall x : R, 0 < Rdist x a < delta /\ ~ Rdist (f x) L < eps))). Proof. intro H. - solve_by_manipulating_negation (). + solve_by_manipulating_negation (fun () => ()). Qed. (* Test 23 *) @@ -221,7 +221,7 @@ Goal (0 = 0) -> (2 = 2) -> ~ (forall eps : R, eps > 0 -> exists delta : R, delta 0 < Rdist x a < delta /\ ~ Rdist (f x) L < eps))). Proof. intros zero_eq_zero two_eq_two H. - solve_by_manipulating_negation (). + solve_by_manipulating_negation (fun () => ()). Qed. Close Scope R_scope. diff --git a/tests/tactics/BothDirections.v b/tests/tactics/BothDirections.v index f45cbff2..f35e0fb3 100644 --- a/tests/tactics/BothDirections.v +++ b/tests/tactics/BothDirections.v @@ -45,5 +45,5 @@ Abort. (** Test 2: This should raise an error, because the goal is not an if and only if*) Goal forall n : nat, n <= n. - assert_raises_error (fun() => We show both directions). + Fail We show both directions. Abort. \ No newline at end of file diff --git a/tests/tactics/BothStatements.v b/tests/tactics/BothStatements.v index 1b74a505..27710e9c 100644 --- a/tests/tactics/BothStatements.v +++ b/tests/tactics/BothStatements.v @@ -52,7 +52,7 @@ Qed. (** Test 2: This should raise an error, because the goal is not a *) Goal forall n : nat, n <= n. Proof. - assert_raises_error (fun() => We show both statements). + Fail We show both statements. Abort. @@ -104,5 +104,5 @@ Abort. Goal forall n : nat, ((n = n) /\ (n + 1 = n + 1)). Proof. intro n. - assert_raises_error (fun () => We show both (n + 2 = n + 2) and (n +3 = n + 3)). + Fail We show both (n + 2 = n + 2) and (n +3 = n + 3). Abort. \ No newline at end of file diff --git a/tests/tactics/Choose.v b/tests/tactics/Choose.v index b8dcac3b..00cab25f 100644 --- a/tests/tactics/Choose.v +++ b/tests/tactics/Choose.v @@ -50,12 +50,12 @@ Qed. (** Test 3: This should raise an error, as the goal is not an exists goal *) Goal forall n : nat, ( ( (n = n) \/ (n + 1 = n + 1) ) -> (n + 1 = n + 1)). intro n. - assert_raises_error (fun() => Choose (n)). + Fail Choose (n). Abort. (** Test 4: This should also raise an error, as the goal is not an exists goal *) Goal forall n : nat, ( ( (n = n) \/ (n + 1 = n + 1) ) -> (n + 1 = n + 1)). intro n. - assert_raises_error (fun() => Choose m := n). + Fail Choose m := n. Abort. diff --git a/tests/tactics/Conclusion.v b/tests/tactics/Conclusion.v index c613a109..28701eac 100644 --- a/tests/tactics/Conclusion.v +++ b/tests/tactics/Conclusion.v @@ -53,8 +53,7 @@ Qed. *) Lemma test_we_conclude_2: True. Proof. - let result () := We conclude that False in - assert_raises_error result. + Fail We conclude that False. Abort. (** * Test 3 @@ -139,6 +138,55 @@ Proof. Fail By test_by_we_conclude_1 we conclude that (1 < 2). Abort. +(** Additional tests 'By ...' clause. *) +(* Test 6: unable to show goal without means required for proof. *) +Variable A B : Prop. +Variable f : A -> B. +Goal A -> B. + intro H. + Fail We conclude that B. +Abort. + +(* Test 7: able to show that goal with means required for proof. *) +Goal A -> B. +Proof. + intro H. + pose f. + We conclude that B. +Qed. + +(* Test 8: able to show goal with additional lemma. *) +Goal A -> B. +Proof. + intro H. + By f we conclude that B. +Qed. + +(* Test 9: unable to show goal with irrelevant lemma. *) +Variable g : B -> A. +Goal B. +Proof. + Fail By g we conclude that B. +Abort. + +(* Test 10: unable to show goal with superfluous lemma. *) +Goal A -> B. + intro H. + pose f. + Fail By g we conclude that B. +Abort. + + +(* Test 11: 'Since ...' works. + For more tests with 'Since ...', see [tests/.../ItHolds.v] *) +Goal A -> B. +Proof. + intro H. + pose f. + Since (A -> B) we conclude that B. +Abort. + + (** * Example for the SUM. Somewhat more realistic context. *) @@ -159,7 +207,7 @@ Qed. Open Scope R_scope. (** * Test 4 -We make an exception on the goal check when the argument is a chain of inequalities +Should accept chain of inequalities as being 'equal' to the goal. *) Goal (3 < 5). We conclude that (& 3 < 4 < 5). @@ -174,23 +222,25 @@ Goal forall eps : R, eps > 0 -> (Rmin (eps / 2) 1 <= eps). intro eps. intro eps_gt_0. assert (& Rmin (eps/2) 1 <= eps/2 <= eps). +cbn; repeat split. auto with wp_core wp_reals. auto with wp_reals. +auto with wp_core wp_reals. Qed. Close Scope R_scope. -(** 'We conclude that' should accept (in nat_scope) (& 3 &<4 &< 5) for (3<5).*) +(** 'We conclude that' should accept (in nat_scope) (& 3 < 4 < 5) for (3<5).*) Goal (3 < 5). We conclude that (& 3 < 4 < 5). Qed. -(** 'We conclude that' should accept (in nat_scope) (& 3 &<4 &<= 5) for (3<5).*) +(** 'We conclude that' should accept (in nat_scope) (& 3 < 4 <= 5) for (3<5).*) Goal (3 < 5). We conclude that (& 3 < 4 <= 5). Qed. -(** 'We conclude that' should accept (in nat_scope) (& 3 &<4 &< 5) for (3<=5).*) +(** 'We conclude that' should accept (in nat_scope) (& 3 <4 < 5) for (3<=5).*) Goal (3 <= 5). We conclude that (& 3 < 4 < 5). Qed. @@ -216,20 +266,20 @@ Proof. Fail We conclude that (0 = 0). Abort. -(** * Test 8 *) -(** Actually tests for [waterprove] automation suboutine, but this seemed like a - convenient place to test. *) -(** Tests whether the error points out which specific (in)equality in the chain does not hold. *) -Local Parameter A : Type. -Local Parameter x y z : A. - -Goal (& x = y = z). -Proof. -Fail We conclude that (& x = y = z). (* Expected: unable to find proof (x = y) *) -Abort. +(** * Test 8 + Test whether we conclude that can solve simple induction proof from + inequality chain. +*) -Goal (x = y) -> (& x = y = z). +Lemma test_induction : + forall F : nat -> nat, (forall k : nat, (F(k+1) = F(k))%nat) -> + forall k : nat, (F(k) = F(0))%nat. Proof. -intro p. -Fail We conclude that (& x = y = z). (* Expected: unable to find proof (y = z) *) -Abort. + intros F H. + We use induction on k. + * We first show the base case (F(0%nat) = F(0%nat)). + We conclude that (F(0) = F(0))%nat. + * We now show the induction step. + intro H1. + We conclude that (& F(k+1) = F(k) = F(0))%nat. +Qed. diff --git a/tests/tactics/Contradiction.v b/tests/tactics/Contradiction.v index b9a8465a..92757a2b 100644 --- a/tests/tactics/Contradiction.v +++ b/tests/tactics/Contradiction.v @@ -56,4 +56,36 @@ Goal forall n : nat, n = n. Proof. We argue by contradiction. Fail Assume that (¬ (for all n : nat, n ≠ n)). -Abort. \ No newline at end of file +Abort. + + +(** Test 4: fails if previous statement is not a contraditcion + to some earlier statement. *) +Variable P Q A : Prop. +Goal P -> Q. + intro H. + Fail Contradiction. +Abort. + +(** Test 5: fails to negate sets and types. *) +Goal nat -> Q. + intro x. + Fail Contradiction. +Abort. + +(* Test 6: Fail to circumvent shielding by attempting to + ask automation to find proof of ~~goal. *) +Goal P -> (P -> A) -> (A -> Q) -> P /\ Q. +Proof. + intros Hp H1 H2. + Fail We conclude that (P /\ Q). + We argue by contradiction. + Assume that (~ (P /\ Q)). + Fail Contradiction. +Abort. + +(** Test 7: Fails if no hypotheses. *) +Goal P. +Proof. + Fail Contradiction. +Abort. diff --git a/tests/tactics/Either.v b/tests/tactics/Either.v index 222f73e8..57388a0d 100644 --- a/tests/tactics/Either.v +++ b/tests/tactics/Either.v @@ -50,7 +50,7 @@ Abort. (** Test 4: This tests to see what error is thrown if we try a nonsense case analysis. *) Goal forall x : R, exists n : nat, INR(n) > x. intro x. - Fail Either (x <= 1) or (0 = 0). + Fail Either (x <= 1) or (x > 2). Abort. Local Lemma sumbool_comm (A B : Prop) : {A} + {B} -> {B} + {A}. @@ -65,12 +65,19 @@ Qed. Also tests whether the hypothesis name from the tactic can be chosen flexibly. *) Goal forall x : R, x >= 0 -> exists n : nat, INR(n) > x. intros x h. -(* assert ({0 = x} + {0 < x}). + (* assert ({0 = x} + {0 < x}). apply sumbool_comm. apply Rle_lt_or_eq_dec. + apply Rge_le. + Locate Rle_lt_or_eq_dec. + apply . + + auto with wp_decidability_reals. *) + (* apply sumbool_comm. + apply Rle_lt_or_eq_dec. auto with wp_reals. *) - (* assert ({0 < x} + {0 = x}). - auto with wp_decidability_reals wp_reals. *) + (*assert ({0 < x} + {0 = x}). *) + (* auto with wp_decidability_reals wp_reals. *) Either (0 = x) or (x > 0). - Case (0 = x). admit. @@ -122,7 +129,9 @@ Goal forall x : R, exists n : nat, INR(n) > x. - Case (0 < x). (* Note that this also works although the literal case is x > 0 =) *) Abort. -(** Test 10: Without loading classical informative decidability, this shouldn't work *) +Waterproof Disable Automation RealsAndIntegers. + +(** Test 10: Without loading classical decidability, this shouldn't work *) Local Parameter A : Prop. Goal False. Fail Either (A) or (~A). @@ -130,10 +139,52 @@ Abort. (** Test 11: Now load classical informative decidability and try again *) -Waterproof Enable Automation Algebra. +Waterproof Enable Automation RealsAndIntegers. Goal False. Either (A) or (~A). Abort. +Waterproof Disable Automation RealsAndIntegers. + +Waterproof Enable Automation ClassicalEpsilon. + +Goal {A} + {~A}. +Either (A) or (~A). +* Case (A). + left. + assumption. +* Case (~A). + right. + assumption. +Abort. + +Goal forall P Q : Prop, P \/ Q -> {P} + {Q}. +Proof. +intros P Q H. +Either (P) or (Q). +* Case (P). + left. + assumption. +* Case (Q). + right. + assumption. +Qed. + +Waterproof Disable Automation ClassicalEpsilon. + Close Scope R_scope. + +Section test_differences_sort_of_goal. + +Variable P : Prop. +Hypothesis P_dec : P \/ ~P. + +(** Test 12: without loading additional databases, we should not be able to get informative excluded middle from decidability *) + +Goal {P} + {~P}. +Proof. +Fail Either (P) or (~P). +Abort. + +End test_differences_sort_of_goal. diff --git a/tests/tactics/Induction.v b/tests/tactics/Induction.v index 448a3bdc..742ea42a 100644 --- a/tests/tactics/Induction.v +++ b/tests/tactics/Induction.v @@ -28,9 +28,9 @@ Require Import Waterproof.Util.Assertions. Goal forall n : nat, (n = n). Proof. We use induction on n. - - Fail We first show the base case, namely (2 = 2). - We first show the base case, namely (0 = 0). - Fail We first show the base case, namely (1 = 1). + - Fail We first show the base case (2 = 2). + We first show the base case (0 = 0). + Fail We first show the base case (1 = 1). reflexivity. - We now show the induction step. Fail We now show the induction step. @@ -43,9 +43,9 @@ Goal (0 = 0) -> forall n : nat, (n = n). Proof. intro n. We use induction on k. - - Fail We first show the base case, namely (2 = 2). - We first show the base case, namely (0 = 0). - Fail We first show the base case, namely (1 = 1). + - Fail We first show the base case (2 = 2). + We first show the base case (0 = 0). + Fail We first show the base case (1 = 1). reflexivity. - We now show the induction step. Fail We now show the induction step. diff --git a/tests/tactics/ItHolds.v b/tests/tactics/ItHolds.v index 97af4f93..ecedeafc 100644 --- a/tests/tactics/ItHolds.v +++ b/tests/tactics/ItHolds.v @@ -135,3 +135,173 @@ Proof. Fail It holds that (x = 2) (h). It holds that (x = 2) (i). Abort. + + +(** Tests for restricted version of 'By ...' clause *) +Variable A B : Prop. +Variable f : A -> B. + +(* Test 5: regular check that assertion works. *) +Goal A -> False. +Proof. + intro H. + pose f. + It holds that B. +Abort. + +(* Test 6: check that assertion works with label *) +Goal A -> False. +Proof. + intro H. + pose f. + It holds that B (i). +Abort. + +(* Test 7: check that assertion fails with label that is already used. *) +Goal A -> False. +Proof. + intro H. + pose f. + Fail It holds that B (H). +Abort. + +(* Test 7b: assertion fails if label already used, even before it is checked whether + assertion is actually valid. + *) +Goal A -> False. +Proof. +intro H. +Fail It holds that B (H). +Abort. + +(* Test 8: 'By ...' succeeds if additional lemma is needed for proof assertion. *) +Goal A -> False. +Proof. + intro H. + By f it holds that B. +Abort. +(* Test 8b: also when lemma is included in local hypotheses. *) +Goal A -> False. +Proof. + intro H. + pose f. + By f it holds that B. +Abort. + +(* Test 9: 'By ...' fails if additional lemma is not enough to prove assertion. *) +Goal False. +Proof. + Fail By f it holds that B. +Abort. + +(* Test 10: 'By ...' fails if additional lemma is not needed for proof assertion. *) +Variable g : B -> A. +Goal A -> False. +Proof. + intro H. + pose f. + Fail By g it holds that B. +Abort. + +(** Tests for 'Since ...' clause. *) +(* Test 11: 'Since ...' works if claimed cause is proven previously. *) +(* Note that no additional hypotheses are added by the Since-tactic. *) +Goal A -> False. +Proof. + intro H. + pose f. + Since (A -> B) it holds that B. +Abort. + +(* Test 12: 'Since ...' fails if claimed cause is not proven previously. *) +Goal A -> False. +Proof. + intro H. + Fail Since (A -> B) it holds that B. +Abort. + +(* + Test excluded, fails because check is no longer performed for + terms in the hypotheses, (see workaround [Wateprove._rwaterprove]). + +(* Test 13: 'Since ...' fails if claimed cause is not necessary to proof final claim. *) +Goal A -> False. +Proof. + intro H. + pose f. + pose g. + Fail Since (B -> A) it holds that B. +Abort. +*) + +(* Test 14: 'Since ...' works with proofs of causal claim provided as external hints. *) +#[local] Hint Resolve f : core. +Goal A -> False. +Proof. + intro H. + Since (A -> B) it holds that B. +Abort. + + +(** Test that references to lemmas cannot be used in the + 'Since ...' tactic, and that statements cannot be used in the + 'By ...' tactic. *) + +(* Test 15: 'By ...' with statement fails.*) +Goal A -> False. +Proof. + intro H. + Fail By (A -> B) it holds that B. + Since (A -> B) it holds that B. +Abort. + +(* Test 16: 'Since ...' with reference fails. *) +Goal A -> False. +Proof. + intro H. + Fail Since f it holds that B. +Abort. + + +(** Tests for workaround occuring anomalies in [_rwaterprove]. *) +(* Test 17: impossible goal with use of lemma in hypotheses. *) +Goal False. +Proof. + assert (A -> B) as f' by admit. + Fail By (f') it holds that B. +Abort. + +(* Test 18: possible goal with right hypothesis provided. *) +Variable C : Prop. +Goal A -> False. +Proof. + intro H. + assert (A -> B) as f' by admit. + assert (B -> C) as g' by admit. + By g' it holds that C. +Abort. + +(* + Test excluded, fails because check is no longer performed for + terms in the hypotheses, (see workaround [Wateprove._rwaterprove]). + +(* Test 19: possible goal with wrong hypothesis provided. *) +Variable D : Prop. +Goal A -> False. +Proof. + intro H. + assert (A -> B) as f' by admit. + assert (B -> C) as g' by admit. + assert D as k' by admit. + Fail By k' it holds that C. +Abort. + +*) + +(* Test 20: works if By-clause is a hypothesis which can be solved with 'assumption'. *) +(* Fails without workaround in [Waterprove._rwaterprove]. *) +Goal A -> (A -> B) -> B. +Proof. + intros Ha Hf. + By Ha it holds that B. +Abort. \ No newline at end of file diff --git a/tests/tactics/ItSuffices.v b/tests/tactics/ItSuffices.v index 75b2e2ed..14ee2ea7 100644 --- a/tests/tactics/ItSuffices.v +++ b/tests/tactics/ItSuffices.v @@ -71,3 +71,45 @@ Proof. (* Clearly this statement isn't helpful in proving the goal! *) Fail By (f_increasing) it suffices to show that (1 + 1 = 2). Abort. + + +(* Test 5: unable to show goal is enough if it does not imply current goal *) +Variable A B : Prop. +Variable g : A -> B. +Goal B. + Fail It suffices to show that A. +Abort. + +(* Test 6: able to show that goal is enough if it implies current goal. *) +Goal B. +Proof. + pose g. + It suffices to show that A. +Abort. + +(* Test 7: able to show goal is enough with additional lemma. *) +Goal B. +Proof. + By g it suffices to show that A. +Abort. + +(* Test 8: unable to goal is enough with irrelevant lemma. *) +Variable h : A. +Goal B. +Proof. + Fail By h it suffices to show that A. +Abort. + +(* Test 9: unable to show goal if lemma is superfluous. *) +Goal B. + pose g. + Fail By h it suffices to show that A. +Abort. + + +(* Test 10: 'Since ...' works. For more tests with 'Since ...', see [tests/.../ItHolds.v] *) +Goal B. +Proof. + pose g. + Since (A -> B) it suffices to show that A. +Abort. \ No newline at end of file diff --git a/tests/tactics/ChooseSuchThat.v b/tests/tactics/Obtain.v similarity index 67% rename from tests/tactics/ChooseSuchThat.v rename to tests/tactics/Obtain.v index 5d7b81fd..acd6bc43 100644 --- a/tests/tactics/ChooseSuchThat.v +++ b/tests/tactics/Obtain.v @@ -27,70 +27,75 @@ Require Import Rtrigo. Require Import Ranalysis. Require Import Integration. Require Import micromega.Lra. -Require Import Max. Require Import Waterproof.Waterproof. Require Import Waterproof.Automation. Require Import Waterproof.Tactics. Require Import Waterproof.Util.Assertions. -(** Test 0: with labeling *) +(** Test 0: works with existence statement*) Goal (exists n : nat, n + 1 = n)%nat -> False. Proof. - intro i. - Obtain n according to (i), so for n : nat it holds that (n + 1 = n)%nat (ii). + intro H. + Obtain such an n. Abort. -(** Test 1: without labeling *) -Goal (exists n : nat, n + 1 = n)%nat -> False. +(** Test 1: works with sigma type *) +Goal {n : nat | (n + 1 = n)%nat} -> False. Proof. - intro i. - Obtain n according to (i), so for n : nat it holds that (n + 1 = n)%nat. + intro H. + Obtain such an n. Abort. -(** Test 2: wrong type variable *) -Goal (exists n : nat, n + 1 = n)%nat -> False. +(** Test 2: Fails with other type. *) +Goal (exists n : nat, n + 1 = n)%nat -> (0 = 0) -> False. Proof. - intro i. - Fail Obtain n3 according to (i), so for n3 : bool it holds that (n3 + 1 = n3)%nat. + intros H1 H2. + Fail Obtain such an n. Abort. -(** Test 3: wrong property *) -Goal (exists n : nat, n + 1 = n)%nat -> False. +(** Test 3: Fails when variable name is already in use. *) +Goal forall m : nat, (exists n : nat, n + 1 = m)%nat -> False. Proof. - intro i. - Fail Obtain n4 according to (i), so for n4 : nat it holds that (n4 + 1 = n4 + 1)%nat. + intros m H. + Fail Obtain such an m. Abort. -(** Test 4: whether statement of existence hypothesis is replecated *) +(** Test 4: existence statement is replaced by copy with the same label. *) Goal (exists n : nat, n + 1 = n)%nat -> False. Proof. intro i. - Obtain n according to (i), so for n : nat it holds that (n + 1 = n)%nat. + Obtain such an n. (* check for yourself! *) Abort. -(** Test 5: tactic also works for destructing sig types *) -Goal {n : nat | (n + 1 = n)%nat} -> False. -Proof. - intro i. - Obtain n according to (i), so for n : nat it holds that (n + 1 = n)%nat. -Abort. -(** Test 6: whether original hypothesis is destructed, so if the goal depends on the +(** Test 5: whether original hypothesis is destructed, so if the goal depends on the specific term of the sigma type, the goal changes as well. As one would expect when using 'destruct .. as [.. ..]'. *) Goal forall p : {n : nat | (n + 1 = n)%nat}, (proj1_sig p = 0)%nat. Proof. intro p. - Obtain n according to (p), so for n : nat it holds that (n + 1 = n)%nat (ii). - simpl. + Obtain such an n. + We need to show that (n = 0)%nat. assert_goal_is constr:((n = 0)%nat). Abort. +(** Test 6: works with specifying statement. *) +Goal (exists n : nat, n + 1 = n)%nat -> False. +Proof. + intro i. + Obtain n according to (i). +Abort. +(** Test 7: fails if specified statement does not exist. *) +Goal (exists n : nat, n + 1 = n)%nat -> False. +Proof. + intro i. + Fail Obtain n according to (ii). +Abort. -(** Test 7: more advanced use of the [Obtain...such that...] in the context of limits of sequences *) +(** Test 8: more advanced use of the [Obtain...such that...] in the context of limits of sequences *) Local Open Scope R_scope. Definition evt_eq_sequences (a b : nat -> R) := (exists k : nat, forall n : nat, (n >= k)%nat -> a n = b n). @@ -100,9 +105,6 @@ Proof. intros. intro. intro. - pose (H0 eps H1) as i. - Fail Obtain N1 according to (i), so for N1 : nat it holds that - (forall n : nat, (n >= N1)%nat -> R_dist (a n) l < eps) (i). - Obtain N1 according to (i), so for N1 : nat it holds that - (forall n : nat, (n >= N1)%nat -> R_dist (a n) l < eps). -Abort. + pose (H0 eps H1). + Obtain such an N1. +Abort. \ No newline at end of file diff --git a/tests/tactics/Take.v b/tests/tactics/Take.v index 68976804..8b8fa16e 100644 --- a/tests/tactics/Take.v +++ b/tests/tactics/Take.v @@ -36,13 +36,13 @@ Abort. (** Test 2: This should raise an error, because the type does not match*) Goal forall n : nat, n <= 2*n. - assert_raises_error (fun () => Take n : bool). + Fail Take n : bool. Abort. (** Test 3: This should raise an error, because [Take] solves forall-quatifiers *) Goal exists n : nat, n <= 2*n. - assert_raises_error (fun() => Take n : nat). + Fail Take n : nat. Abort. (** Test 4: Multi argument testcase *) @@ -83,7 +83,7 @@ Abort. (** Test 7: not allowed to introduce so many bools *) Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2. - assert_raises_error (fun () => Take a, b, c, d, e: bool). + Fail Take a, b, c, d, e : bool. Abort. (** Test 8: look how crazy many vars we can introduce*) @@ -109,7 +109,7 @@ Abort. This should raise an error, as the order of introducing variables is different. *) Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2. - assert_raises_error (fun() => Take y, u: bool and a, b, c : nat). + Fail Take y, u : bool and a, b, c : nat. Abort. (** Test 11: Attempting to show an implication should be rejected. *) diff --git a/tests/tactics/ToShow.v b/tests/tactics/ToShow.v index f8eff827..588b5624 100644 --- a/tests/tactics/ToShow.v +++ b/tests/tactics/ToShow.v @@ -54,20 +54,6 @@ Qed. (** Third test: this should raise an error, as the wrong goal is supplied. *) Lemma two_is_two: 2 = 2. Proof. - assert_raises_error (fun () => We need to show (1 = 1)). + Fail We need to show (0 = 2). reflexivity. -Qed. - -(** Fourth test: the goal should be rewritten, if not the proof will fail. *) -Lemma two_is_two_rewrite_goal : 1 + 1 = 1 + 1. -Proof. - set (k := 2). - assert (H : k = 2). - { - trivial. - } - assert_raises_error (fun () => rewrite H). - We need to show that (k = k). - rewrite H. - trivial. Qed. \ No newline at end of file diff --git a/tests/tactics/Unfold.v b/tests/tactics/Unfold.v index 12b3295f..b87903da 100644 --- a/tests/tactics/Unfold.v +++ b/tests/tactics/Unfold.v @@ -24,56 +24,134 @@ Require Import Waterproof.Automation. Require Import Waterproof.Tactics. Require Import Waterproof.Util.Assertions. -Definition some_function (x: nat) := 2 * x. -Definition other_function (x: nat) := 2 * x. - -(** * Test 3 - Unfold functions IN THE GOAL to show that its definition - proves the goal. -*) -Lemma test_unfold_3: forall (x:nat), some_function (other_function x) = 2*(2*x). - intros x. - Expand the definition of some_function. - Fail That is, write the goal as (0 = 0). - That is, write the goal as (2 * other_function x = 2 * (2 * x)). - Expand the definition of other_function. - That is, write the goal as (2*(2*x) = 2*(2*x)). - reflexivity. -Qed. - - -(** * Test 4 - Unfold functions IN THE GOAL to show that its definition - proves the goal, using we_need_to_show notation. -*) -Lemma test_unfold_4: forall (x:nat), some_function (other_function x) = 2*(2*x). - intros x. - Expand the definition of some_function. - We need to show (2 * other_function x = 2 * (2 * x)). - reflexivity. -Qed. - - -(** * Test 5 - Unfold functions IN A HYPOTHESIS to show that its definition - proves the goal. -*) -Lemma test_unfold_5: forall (x a:nat), some_function (other_function x) = a -> False. - intros x a i. - Fail Expand the definition of some_function, other_function in (ii). - Expand the definition of some_function, other_function in (i). - Fail That is, write (ii) as (2*(2*x) = a). - Fail That is, write (x) as (2*(2*x) = a). - Fail That is, write (i) as (0 = 0). - That is, write (i) as (2*(2*x) = a). -Abort. - -(** * Test 6 - Unfold functions IN A HYPOTHESIS to show that its definition - proves the goal. -*) -Lemma test_unfold_6: forall (x a:nat), some_function (other_function x) = a -> False. - intros x a i. - Expand the definition of some_function, other_function in (i). - That is, write (i) as ( 2 * (2 * x) = a ). -Abort. \ No newline at end of file +Definition foo : nat := 0. + +(* Tests general unfolding: *) + +(* Test 1: unfold term in statement and throws an error suggesting + to remove line to continue. *) +Goal False. +Proof. + Fail Expand the definition of foo in foo. +Abort. + +(* Test 2: unfold term in statement matching goal, and throws an error suggesting + to replace line with 'We need to show that ...'. *) +Goal foo = 1. +Proof. + Fail Expand the definition of foo in (foo = 1). +Abort. + +(* Test 3: unfold term in statement matching a hypothesis and throws an error suggesting + to replace line with 'It holds that ...'. *) +Goal (foo = 0) -> (foo = 2) -> (foo = 1). +Proof. + intros. + Fail Expand the definition of foo in (foo = 0). + Fail Expand the definition of foo in (foo = 2). +Abort. + +(* Test 4: fails to unfold term in statment without term. *) +Goal False. +Proof. + Fail Expand the definition of foo in 0. +Abort. + + + +(* Tests framework expand the definition. *) +Local Ltac2 unfold_foo (statement : constr) := eval unfold foo in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "foo2" "in" statement(constr) := + unfold_in_statement unfold_foo (Some "foo2") statement. + +(* Test 5: unfold term in statement and throws an error suggesting + to remove line to continue. *) +Goal False. +Proof. + Fail Expand the definition of foo2 in foo. +Abort. + +(* Test 6: unfold term in statement matching goal, and throws an error suggesting + to replace line with 'We need to show that ...'. *) +Goal foo = 1. +Proof. + Fail Expand the definition of foo2 in (foo = 1). +Abort. + +(* Test 7: unfold term in statement matching a hypothesis and throws an error suggesting + to replace line with 'It holds that ...'. *) +Goal (foo = 0) -> (foo = 2) -> (foo = 1). +Proof. + intros. + Fail Expand the definition of foo2 in (foo = 0). + Fail Expand the definition of foo2 in (foo = 2). +Abort. + +(* Test 8: fails to unfold term in statment without term. *) +Goal False. +Proof. + Fail Expand the definition of foo2 in 0. +Abort. + + +(** Check unfolding method that does not throw an error. + Meant for internal use by custom Waterproof editor. *) +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "foo2" "in" statement(constr) := + unfold_in_statement_no_error unfold_foo (Some "foo2") statement. + +(* Test 9: unfold term in statement. *) +Goal False. +Proof. + _internal_ Expand the definition of foo2 in foo. +Abort. + +(* Test 10: unfold term in statement matching goal, and prints a message suggesting + to replace line with 'We need to show that ...'. *) +Goal foo = 1. +Proof. + _internal_ Expand the definition of foo2 in (foo = 1). +Abort. + +(* Test 11: unfold term in statement matching a hypothesis and prints a message suggesting + to replace line with 'It holds that ...'. *) +Goal (foo = 0) -> (foo = 2) -> (foo = 1). +Proof. + intros. + _internal_ Expand the definition of foo2 in (foo = 0). + _internal_ Expand the definition of foo2 in (foo = 2). +Abort. + +(* Test 12: fails to unfold term in statment without term. *) +Goal False. +Proof. + _internal_ Expand the definition of foo2 in 0. +Abort. + +(* Test 13: internal unfold term in statement and throws an error suggesting + to remove line to continue. *) +Goal False. +Proof. + _internal_ Expand the definition of foo in foo. +Abort. + +(* Test 14: internal unfold term in statement matching goal, and throws an error suggesting + to replace line with 'We need to show that ...'. *) +Goal foo = 1. +Proof. + _internal_ Expand the definition of foo in (foo = 1). +Abort. + +(* Test 15: internal unfold term in statement matching a hypothesis and throws an error suggesting + to replace line with 'It holds that ...'. *) +Goal (foo = 0) -> (foo = 2) -> (foo = 1). +Proof. + intros. + _internal_ Expand the definition of foo in (foo = 0). + _internal_ Expand the definition of foo in (foo = 2). +Abort. + +(* Test 16: internal unfold fails to unfold term in statment without term. *) +Goal False. +Proof. + _internal_ Expand the definition of foo in 0. +Abort. diff --git a/tests/test-folder.py b/tests/test-folder.py new file mode 100644 index 00000000..8dd827d3 --- /dev/null +++ b/tests/test-folder.py @@ -0,0 +1,41 @@ +import subprocess +import os +import glob +import json +import argparse + +if __name__ == "__main__": + PARSER = argparse.ArgumentParser( + description='Test all mv files in a directory recursively against the current library.') + PARSER.add_argument('folder', type=str, + help='The directory to test') + PARSER.add_argument('--exclude', type=str, + help='(Not implemented) file pattern to exclude') + + ARGS = PARSER.parse_args() + FOLDER = ARGS.folder + EXCLUDE = ARGS.exclude + + print(FOLDER) + + failed = False + print("Current working directory:") + print(os.getcwd()) + for filename in glob.iglob('**/*.mv', recursive=True, root_dir=FOLDER): + print(filename) + result = subprocess.run(['fcc', '--root=../../../', f'{FOLDER}/{filename}'], + capture_output=True) + if result.returncode != 0: + raise Exception(f"Compilation of file {filename} has failed\n{result.stderr}") + for filename in glob.iglob('**/*.diags', recursive=True, root_dir=FOLDER): + print(filename) + with open(f'{FOLDER}/{filename}') as file: + contents = file.read().replace('}\n{','},{') + diags = json.loads('[' + contents + ']') + for diag in diags: + if(diag['severity'] <= 1): + print(filename) + print(diag) + failed = True + if failed: + raise Exception("The tests against the exercises have failed") diff --git a/tests/util/MessagesToUser.v b/tests/util/MessagesToUser.v new file mode 100644 index 00000000..b9195a66 --- /dev/null +++ b/tests/util/MessagesToUser.v @@ -0,0 +1,27 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Waterproof.Waterproof. +Require Import Waterproof.Util.MessagesToUser. + +Lemma test : 0 = 0. +Proof. + warn (Message.of_string "This warning _should_ be printed."). + Fail throw (Message.of_string "This error _should_ be raised."). +Abort. diff --git a/theories/Automation/Framework.v b/theories/Automation/Framework.v index 24727f57..d7b2c0b6 100644 --- a/theories/Automation/Framework.v +++ b/theories/Automation/Framework.v @@ -16,6 +16,5 @@ (* *) (******************************************************************************) -(* Declare ML Module "coq-core.plugins.ltac2". *) From Ltac2 Require Import Init. Declare ML Module "waterproof:coq-waterproof.databases". \ No newline at end of file diff --git a/theories/Automation/Hints.v b/theories/Automation/Hints.v index fb2bd321..938b1907 100644 --- a/theories/Automation/Hints.v +++ b/theories/Automation/Hints.v @@ -31,28 +31,37 @@ Require Import Sets.Ensembles. Require Import Chains. Require Import Libs.Negation. Require Import Libs.Reals. +Require Import Libs.Logic.InformativeEpsilon. +Require Import Libs.Logic.ConstructiveLogic. (** * Waterproof core *) Create HintDb wp_core. - #[export] Hint Extern 1 ( _ = _ ) => cbn; ltac2:(simpl_ineq_chains ()); ltac2:(split_conjunctions ()) : wp_core. - #[export] Hint Resolve eq_sym : wp_core. + (* #[export] Hint Extern 1 ( _ = _ ) => progress(cbn; ltac2:(simpl_ineq_chains ()); ltac2:(split_conjunctions ())) : wp_core. *) + (* #[export] Hint Resolve eq_sym : wp_core. *) + (* including [eq_sym] slows down automation significantly, and we can apparantly do without :) + [eq_sym] is still included in [wp_reals] *) #[export] Hint Resolve f_equal : wp_core. #[export] Hint Resolve f_equal2 : wp_core. - #[export] Hint Extern 1 ( _ = _ ) => congruence : wp_core. - #[export] Hint Extern 0 (total_statement _) => repeat split; cbn : wp_core. + (* #[export] Hint Extern 2 ( _ = _ ) => congruence 20 : wp_core. *) + #[export] Hint Extern 2 => progress ltac2:(simpl_ineq_chains ()) : wp_core. + #[export] Hint Extern 1 ( _ = _ ) => progress ltac2:(simpl_ineq_chains ()); congruence 20 : wp_core. + +(** * Definitions *) + +Create HintDb wp_definitions. (** * Classical logic *) Create HintDb wp_classical_logic. +(* Taken care of by [wp_negation_logic] / [wp_negation_...] *) + +(** * Logical negation *) + +Create HintDb wp_negation_logic. - #[export] Hint Resolve not_ex_all_not : wp_classical_logic. - #[export] Hint Resolve ex_not_not_all : wp_classical_logic. - #[export] Hint Resolve all_not_not_ex : wp_classical_logic. - #[export] Hint Resolve not_all_not_ex : wp_classical_logic. - #[export] Hint Resolve not_all_ex_not : wp_classical_logic. - #[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation ()) : wp_classical_logic. + #[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation (fun () => ())) : wp_negation_logic. (** * Constructive logic *) @@ -62,20 +71,38 @@ Create HintDb wp_constructive_logic. #[export] Hint Resolve ex_not_not_all : wp_constructive_logic. #[export] Hint Resolve all_not_not_ex : wp_constructive_logic. +(** * Decidability based on classical epsilon*) + +Create HintDb wp_decidability_epsilon. + + #[export] Hint Resolve excluded_middle_informative : wp_decidability_epsilon. + #[export] Hint Resolve informative_or_lift : wp_decidability_epsilon. (** * Classical logic decidability *) Create HintDb wp_decidability_classical. - #[export] Hint Resolve excluded_middle_informative : wp_decidability_classical. + #[export] Hint Resolve classic : wp_decidability_classical. + +(** * Constructive decidability *) + +Create HintDb wp_decidability_constructive. + #[export] Hint Resolve make_sumbool_uninformative_1 : wp_decidability_constructive. + #[export] Hint Resolve make_sumbool_uninformative_2 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_1 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_2 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_3 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_4 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_5 : wp_decidability_constructive. + #[export] Hint Resolve make_sumtriad_uninformative_6 : wp_decidability_constructive. (** * Natural numbers decidability *) -Create HintDb wp_decidability_classical. +Create HintDb wp_decidability_nat. #[export] Hint Resolve Nat.eq_dec : wp_decidability_nat. - + #[export] Hint Extern 1 => lia : wp_decidability_nat. (** * Real numbers decidability *) @@ -86,6 +113,7 @@ Create HintDb wp_decidability_reals. We cannot do the same for >= as it is not defined as <=. *) #[export] Hint Extern 1 => unfold Rgt : wp_decidability_reals. + #[export] Hint Extern 1 => lra : wp_decidability_reals. #[export] Hint Resolve Req_EM_T : wp_decidability_reals. #[export] Hint Resolve Rlt_le_dec : wp_decidability_reals. @@ -100,6 +128,12 @@ Create HintDb wp_decidability_reals. (** <>, <> or <> *) #[export] Hint Resolve total_order_T : wp_decidability_reals. + (** lemmas to relate <= with >= and < with > *) + #[export] Hint Resolve Rge_le : wp_decidability_reals. + #[export] Hint Resolve Rle_ge : wp_decidability_reals. + #[export] Hint Resolve Rgt_lt : wp_decidability_reals. + #[export] Hint Resolve Rlt_gt : wp_decidability_reals. + (** * Real numbers' addition and multiplication *) @@ -318,60 +352,66 @@ Create HintDb wp_eq_exp. Create HintDb wp_integers. - #[export] Hint Extern 3 ( _ = _ ) => cbn; ltac2:(simpl_ineq_chains ()); ring : wp_integers. - #[export] Hint Extern 3 ( @eq nat _ _) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. - #[export] Hint Extern 3 ( le _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. - #[export] Hint Extern 3 ( ge _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. - #[export] Hint Extern 3 ( lt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. - #[export] Hint Extern 3 ( gt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. + #[export] Hint Extern 3 ( _ = _ ) => ring : wp_integers. + #[export] Hint Extern 1 ( @eq nat _ _) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. + #[export] Hint Extern 1 ( le _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. + #[export] Hint Extern 1 ( ge _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. + #[export] Hint Extern 1 ( lt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. + #[export] Hint Extern 1 ( gt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lia : wp_integers. (** * Integer negation *) Create HintDb wp_negation_int. - #[export] Hint Resolve Zle_not_lt : wp_negation_int. - #[export] Hint Resolve Zlt_not_le : wp_negation_int. - #[export] Hint Resolve Zle_not_gt : wp_negation_int. - #[export] Hint Resolve Zgt_not_le : wp_negation_int. - #[export] Hint Resolve Znot_lt_ge : wp_negation_int. - #[export] Hint Resolve Znot_lt_ge : wp_negation_int. - #[export] Hint Resolve Znot_gt_le : wp_negation_int. - #[export] Hint Resolve Znot_le_gt : wp_negation_int. + #[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation (fun () => ltac1:(lia))) : wp_negation_int. + (** * Natural number negation *) Create HintDb wp_negation_nat. - #[export] Hint Resolve Nat.le_ngt : wp_negation_nat. - #[export] Hint Resolve not_lt : wp_negation_nat. - #[export] Hint Resolve not_le : wp_negation_nat. + #[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation (fun () => ltac1:(lia))) : wp_negation_nat. (** * Real numbers *) Create HintDb wp_reals. - #[export] Hint Extern 3 => ltac2:(simpl_member_subset ()); lra : wp_reals. - #[export] Hint Extern 3 (pred R _ _) => simpl; lra : wp_reals. + #[export] Hint Extern 2 => ltac2:(simpl_member_subset ()); lra : wp_reals. - #[export] Hint Extern 3 ( @eq R _ _ ) => ltac2:(simpl_ineq_chains ()); field : wp_reals. + #[export] Hint Extern 1 (pred R _ _) => simpl; lra : wp_reals. - #[export] Hint Extern 3 ( Rle _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 ( Rge _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 ( Rlt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 ( Rgt _ _ ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. + #[export] Hint Extern 3 ( @eq R _ _ ) => field : wp_reals. - #[export] Hint Extern 3 (~ (Rle _ _) ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 (~ (Rge _ _) ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 (~ (Rlt _ _) ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 (~ (Rgt _ _) ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. - #[export] Hint Extern 3 (~ (@eq R _ _ ) ) => cbn; ltac2:(simpl_ineq_chains ()); lra : wp_reals. + #[export] Hint Extern 1 ( Rle _ _ ) => lra : wp_reals. + #[export] Hint Extern 1 ( Rge _ _ ) => lra : wp_reals. + #[export] Hint Extern 1 ( Rlt _ _ ) => lra : wp_reals. + #[export] Hint Extern 1 ( Rgt _ _ ) => lra : wp_reals. + + #[export] Hint Extern 1 (~ (Rle _ _) ) => lra : wp_reals. + #[export] Hint Extern 1 (~ (Rge _ _) ) => lra : wp_reals. + #[export] Hint Extern 1 (~ (Rlt _ _) ) => lra : wp_reals. + #[export] Hint Extern 1 (~ (Rgt _ _) ) => lra : wp_reals. + #[export] Hint Extern 1 (~ (@eq R _ _ ) ) => lra : wp_reals. - #[export] Hint Extern 3 ( Rle _ _ ) => cbn; nra : wp_reals. - #[export] Hint Extern 3 ( Rge _ _ ) => cbn; nra : wp_reals. - #[export] Hint Extern 3 ( Rlt _ _ ) => cbn; nra : wp_reals. - #[export] Hint Extern 3 ( Rgt _ _ ) => cbn; nra : wp_reals. + #[export] Hint Extern 2 ( @eq R _ _ ) => ltac2:(crush_R_abs_min_max ()): wp_reals. + + #[export] Hint Extern 2 ( Rle _ _ ) => ltac2:(crush_R_abs_min_max ()) : wp_reals. + #[export] Hint Extern 2 ( Rge _ _ ) => ltac2:(crush_R_abs_min_max ()) : wp_reals. + #[export] Hint Extern 2 ( Rlt _ _ ) => ltac2:(crush_R_abs_min_max ()) : wp_reals. + #[export] Hint Extern 2 ( Rgt _ _ ) => ltac2:(crush_R_abs_min_max ()) : wp_reals. + + #[export] Hint Extern 3 (~ (Rle _ _) ) => cbn; ltac2:(crush_R_abs_min_max ()) : wp_reals. + #[export] Hint Extern 3 (~ (Rge _ _) ) => cbn; ltac2:(crush_R_abs_min_max ()); lra : wp_reals. + #[export] Hint Extern 3 (~ (Rlt _ _) ) => cbn; ltac2:(crush_R_abs_min_max ()); lra : wp_reals. + #[export] Hint Extern 3 (~ (Rgt _ _) ) => cbn; ltac2:(crush_R_abs_min_max ()); lra : wp_reals. + #[export] Hint Extern 3 (~ (@eq R _ _ ) ) => cbn; ltac2:(crush_R_abs_min_max ()); lra : wp_reals. + + #[export] Hint Extern 3 ( Rle _ _ ) => nra : wp_reals. + #[export] Hint Extern 3 ( Rge _ _ ) => nra : wp_reals. + #[export] Hint Extern 3 ( Rlt _ _ ) => nra : wp_reals. + #[export] Hint Extern 3 ( Rgt _ _ ) => nra : wp_reals. #[export] Hint Resolve eq_sym : wp_reals. #[export] Hint Resolve false_Req : wp_reals. @@ -391,6 +431,11 @@ Create HintDb wp_reals. #[export] Hint Resolve Rmin_right : wp_reals. #[export] Hint Resolve Rmin_glb : wp_reals. #[export] Hint Resolve Rmin_glb_lt : wp_reals. + (** lemmas to relate <= with >= and < with > *) + #[export] Hint Resolve Rge_le : wp_reals. + #[export] Hint Resolve Rle_ge : wp_reals. + #[export] Hint Resolve Rgt_lt : wp_reals. + #[export] Hint Resolve Rlt_gt : wp_reals. #[export] Hint Resolve div_sign_flip : wp_reals. #[export] Hint Resolve div_pos : wp_reals. @@ -421,22 +466,7 @@ Create HintDb wp_reals. Create HintDb wp_negation_reals. - #[export] Hint Resolve Rnot_le_lt : wp_negation_reals. - #[export] Hint Resolve Rnot_ge_gt : wp_negation_reals. - #[export] Hint Resolve Rnot_le_gt : wp_negation_reals. - #[export] Hint Resolve Rnot_ge_lt : wp_negation_reals. - #[export] Hint Resolve Rnot_lt_le : wp_negation_reals. - #[export] Hint Resolve Rnot_gt_le : wp_negation_reals. - #[export] Hint Resolve Rnot_gt_ge : wp_negation_reals. - #[export] Hint Resolve Rnot_lt_ge : wp_negation_reals. - - #[export] Hint Resolve Rlt_not_le : wp_negation_reals. - #[export] Hint Resolve Rgt_not_le : wp_negation_reals. - #[export] Hint Resolve Rlt_not_ge : wp_negation_reals. - #[export] Hint Resolve Rle_not_lt : wp_negation_reals. - #[export] Hint Resolve Rge_not_lt : wp_negation_reals. - #[export] Hint Resolve Rle_not_gt : wp_negation_reals. - #[export] Hint Resolve Rge_not_gt : wp_negation_reals. + #[export] Hint Extern 1 => ltac2:(solve_by_manipulating_negation (fun () => ltac1:(lra))) : wp_negation_reals. (** * Sets *) @@ -450,4 +480,5 @@ Create HintDb wp_sets. Create HintDb wp_intuition. - #[export] Hint Extern 7 => intuition (auto 2 with core): wp_intuition. \ No newline at end of file + #[export] Hint Extern 8 => intuition (auto 2 with core): wp_intuition. + \ No newline at end of file diff --git a/theories/Chains/Manipulation.v b/theories/Chains/Manipulation.v index 5103cbab..ef650167 100644 --- a/theories/Chains/Manipulation.v +++ b/theories/Chains/Manipulation.v @@ -26,13 +26,13 @@ Require Export Notations.Sets. Writes out an inequality chain as a big conjunction. *) Ltac2 simpl_ineq_chains () := - repeat ( + progress (repeat ( (* TODO: do this in a more structured way *) match! goal with | [ h : total_statement _ |- _ ] => cbn in $h end - ). + )). (** Iteratively splits all conjunctions in the hypothesis into individual statements. @@ -53,9 +53,9 @@ Ltac2 split_conjunctions () := where $A := {x : X | (P x) holds}$. *) Ltac2 simpl_member_subset () := - repeat ( + progress (repeat ( match! goal with | [ h : (pred _ _) _ |- _ ] => simpl in $h | [ |- _ ] => () end - ). \ No newline at end of file + )). \ No newline at end of file diff --git a/theories/Libs/Analysis.v b/theories/Libs/Analysis.v index 121c9f3c..7ad6e83a 100644 --- a/theories/Libs/Analysis.v +++ b/theories/Libs/Analysis.v @@ -25,7 +25,7 @@ Require Export Libs.Analysis.SequencesMetric. Require Export Libs.Analysis.Sequences. Require Export Libs.Analysis.SequentialAccumulationPoints. Require Export Libs.Analysis.Series. -Require Export Libs.Analysis.SubsequencesInduction. Require Export Libs.Analysis.SubsequencesMetric. +Require Export Libs.Analysis.StrongInductionIndexSequence. Require Export Libs.Analysis.Subsequences. Require Export Libs.Analysis.SupAndInf. diff --git a/theories/Libs/Analysis/ContinuityDomainNat.v b/theories/Libs/Analysis/ContinuityDomainNat.v index 8426be82..3daac0e3 100644 --- a/theories/Libs/Analysis/ContinuityDomainNat.v +++ b/theories/Libs/Analysis/ContinuityDomainNat.v @@ -18,15 +18,104 @@ Require Import Coq.Reals.Reals. +Require Import Tactics. Require Import Automation. Require Import Libs.Negation. Require Import Notations. -Require Import Tactics. Waterproof Enable Automation RealsAndIntegers. Open Scope R_scope. +(** Hints *) + +(** TODO: move these lemmas somewhere else *) + +Lemma aux1 : for all n m : ℕ, (n = m) ⇒ |m - n| = |n - n|. +Proof. + Take n, m : ℕ. + Assume that (n = m). + We conclude that (& |m - n| = |m - m| = m - m = 0 = n - n = |n - n|). +Qed. +#[export] Hint Resolve aux1 : wp_reals. + +Lemma Rabs_n_min_m : forall m n : ℕ, (m <= n)%nat -> | n - m | = n - m. +Proof. + Take m, n : nat. + Assume that (m <= n)%nat. + It holds that (m <= n). + We conclude that (| n - m | = n - m). +Qed. + +#[export] Hint Resolve Rabs_n_min_m : wp_reals. + +Lemma Rabs_m_lt_n : forall m n : ℕ, (m < n)%nat -> 1 <= | n - m |. +Proof. + Take m, n : nat. + Assume that (m < n)%nat. + It holds that (m <= n). + It holds that ( | n - m | = n - m). + It holds that (m + 1 <= n)%nat. + It holds that (INR m + INR 1 = INR (m + 1)%nat). + It holds that ((m + 1)%nat <= n). + It holds that (& INR m + INR 1 = INR (m + 1)%nat <= n). + We conclude that (1 <= | n - m |). +Qed. + +#[export] Hint Resolve Rabs_m_lt_n : wp_reals. + +Lemma nat_not_equal_dist_larger_one : for all n m : ℕ, (n ≠ m) -> (1 ≤ | m - n |). +Proof. + Take n, m : ℕ. + Assume that (n ≠ m). + assert (n > m ∨ n < m)%nat as i by (apply Nat.lt_gt_cases; auto). + Because (i) either (n > m)%nat or (n < m)%nat holds. + + Case (n > m)%nat. + It holds that (| n - m | = | m - n | ). + It holds that (1 <= | n - m |). + We conclude that (1 <= | m - n |). + + Case (m > n)%nat. + We conclude that (1 <= | m - n |). +Qed. +#[export] Hint Resolve nat_not_equal_dist_larger_one : wp_reals. + +Lemma nat_dist_larger_zero_not_equal : + forall n m : nat, 0 < | n - m | -> ~(n = m). +Proof. +Take n, m : nat. +Assume that (0 < | n - m | ). +Either (n = m)%nat or (~(n= m))%nat. ++ Case (n = m)%nat. + It holds that (| m - n| = 0). + Contradiction. ++ Case (~(n = m)). + We conclude that (~(n = m)). +Qed. +#[export] Hint Resolve nat_dist_larger_zero_not_equal : wp_reals. + +Lemma nat_dist_less_than_one_iff_equal : + forall n m : ℕ, (n = m) <-> (| m - n | < 1). +Proof. + Take n, m : nat. + We show both directions. + - We need to show that (n = m ⇨ |m - n| < 1). + Assume that (n = m). + We argue by contradiction. + Assume that (~ | m - n | < 1). + It holds that (1 <= | m - n|). + It holds that (~(n=m)). + Contradiction. + - We need to show that (| m - n | < 1 -> n = m). + Assume that (| m - n| < 1). + We argue by contradiction. + Assume that (~ (n = m)). + It holds that (1 <= | m - n|). + Contradiction. +Qed. + +#[export] Hint Resolve <- nat_dist_less_than_one_iff_equal : wp_reals. +#[export] Hint Resolve -> nat_dist_less_than_one_iff_equal : wp_reals. + (** Definitions *) Section metricspace. Variable X : Metric_Space. @@ -49,86 +138,110 @@ Definition limit_in_point (f : ℕ → X) (a : ℕ) (L : X) := Definition is_continuous_in (f : ℕ → X) (a : ℕ) := ((is_accumulation_point a) ∧ (limit_in_point f a (f a))) ∨ (is_isolated_point a). -End metricspace. - -(** Hints *) -Lemma aux1 : for all n m : ℕ, (n = m) ⇒ |m - n| = |n - n|. +Theorem alt_char_continuity : + ∀ f : ℕ → X, ∀ a : ℕ, + is_continuous_in f a ⇔ ∀ ε : R, ε > 0 ⇒ ∃ δ : R, (δ > 0) ∧ (∀ x : ℕ, 0 < | x - a | < δ ⇒ dist X (f(x)) (f(a)) < ε). Proof. - Take n, m : ℕ. - Assume that (n = m). - We conclude that (& |m - n| = |m - m| = m - m = 0 = n - n = |n - n|). + Take h : (ℕ → X). + Take a : ℕ. + We show both directions. + * We need to show that (is_continuous_in(h, a) ⇨ for all ε : ℝ, + ε > 0 ⇨ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℕ, + 0 < |x - a| < δ ⇨ dist(X, h(x), h(a)) < ε)). + Assume that (is_continuous_in h a). + Take ε : R. + Assume that (ε > 0). + Choose δ := (1/2). + We show both statements. + - We conclude that (δ > 0). + - We need to show that (for all x : nat, + 0 < |x - a| < δ ⇨ dist(X, h(x), h(a)) < ε). + Take x : nat. + Assume that (0 < | x - a | < δ). + Either (x = a) or (~(x=a)). + + Case (x = a). + It holds that (| x - a | = 0). + Contradiction. + + Case (~(x = a)). + It holds that (1 ≤ | x - a |). + Contradiction. + * We need to show that ((for all ε : ℝ, + ε > 0 ⇨ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℕ, + 0 < |x - a| < δ ⇨ dist(X, h(x), h(a)) < ε)) ⇨ is_continuous_in(h, a)). + Assume that ((for all ε : ℝ, + ε > 0 ⇨ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℕ, + 0 < |x - a| < δ ⇨ dist(X, h(x), h(a)) < ε))). + We need to show that (is_accumulation_point(a) ∧ limit_in_point(h, a, h(a)) ∨ is_isolated_point(a)). + It suffices to show that (is_isolated_point a). + unfold is_isolated_point. + We need to show that (∃ ε : ℝ, ε > 0 ∧ (for all n : ℕ, |n - a| = 0 ∨ ε ≤ |n - a|)). + Choose ε := (1/2). + We show both statements. + - We conclude that (ε > 0). + - We need to show that (for all n : ℕ, |n - a| = 0 ∨ ε ≤ |n - a|). + Take n : nat. + Either (n = a) or (~(n=a)). + + Case (n = a). + It suffices to show that (|n - a| = 0). + We conclude that (|n - a| = 0). + + Case (~(n=a)). + It suffices to show that ((1/2) ≤ | n - a |). + It holds that (1 ≤ | n - a |). + We conclude that (1/2 ≤ | n - a | ). Qed. -#[export] Hint Resolve aux1 : wp_reals. -(** Useful lemma *) -Lemma useful_lemma : for all n m : ℕ, (n ≠ m) ⇒ (1 ≤ | m - n |). -Proof. - Take n, m : ℕ. Assume that (n ≠ m). - assert (n > m ∨ n < m)%nat as i by (apply Nat.lt_gt_cases; auto). - Because (i) either (n > m)%nat or (n < m)%nat holds. - + Case (n > m)%nat. - It holds that (n ≥ S m)%nat. - By S_INR it holds that (m + 1 = S m). - It holds that (m + 1 - m = S m - m). - It holds that ((S m) ≤ n). - It holds that ((S m) - m ≤ n - m). - We conclude that - (& 1 = m + 1 - m = (S m) - m ≤ n - m = | n - m | = | m - n | ). - + Case (n < m)%nat. - It holds that (S n ≤ m)%nat. - By S_INR it holds that (n + 1 = S n). - It holds that (n + 1 - n = S n - n). - It holds that ((S n) ≤ m). - It holds that ((S n) - n ≤ m - n). - We conclude that (& 1 = n + 1 - n = (S n) - n ≤ m - n = |m - n|). -Qed. +End metricspace. + +#[export] Hint Resolve -> alt_char_continuity : wp_reals. +#[export] Hint Resolve <- alt_char_continuity : wp_reals. (** Notations *) Notation "a 'is' 'an' '_accumulation' 'point_'" := (is_accumulation_point a) (at level 68). Notation "a 'is' 'an' 'accumulation' 'point'" := (is_accumulation_point a) (at level 68, only parsing). -Local Ltac2 unfold_is_accumulation_point () := unfold is_accumulation_point. - -Local Ltac2 unfold_is_accumulation_point_in (h : ident) := unfold is_accumulation_point in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "accumulation" "point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_accumulation_point unfold_is_accumulation_point_in cl. - +Local Ltac2 unfold_acc_point (statement : constr) := eval unfold is_accumulation_point in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "accumulation" "point" "in" statement(constr) := + unfold_in_statement unfold_acc_point (Some "accumulation point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "accumulation" "point" "in" statement(constr) := + unfold_in_statement_no_error unfold_acc_point (Some "accumulation point") statement. Notation "a 'is' 'an' '_isolated' 'point_'" := (is_isolated_point a) (at level 68). Notation "a 'is' 'an' 'isolated' 'point'" := (is_isolated_point a) (at level 68, only parsing). -Local Ltac2 unfold_is_isolated_point () := unfold is_isolated_point. +Local Ltac2 unfold_isol_point (statement : constr) := eval unfold is_isolated_point in $statement. -Local Ltac2 unfold_is_isolated_point_in (h : ident) := unfold is_isolated_point in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "isolated" "point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_isolated_point unfold_is_isolated_point_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "isolated" "point" "in" statement(constr) := + unfold_in_statement unfold_isol_point (Some "isolated point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "isolated" "point" "in" statement(constr) := + unfold_in_statement_no_error unfold_isol_point (Some "isolated point") statement. Notation "'_limit_' 'of' f 'in' a 'is' L" := (limit_in_point _ f a L) (at level 68). Notation "'limit' 'of' f 'in' a 'is' L" := (limit_in_point _ f a L) (at level 68, only parsing). -Local Ltac2 unfold_limit_in_point () := unfold limit_in_point. - -Local Ltac2 unfold_limit_in_point_in (h : ident) := unfold limit_in_point in $h. +Local Ltac2 unfold_lim_in_point (statement : constr) := eval unfold limit_in_point in $statement. -Ltac2 Notation "Expand" "the" "definition" "of" "limit" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_limit_in_point unfold_limit_in_point_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "limit" "in" statement(constr) := + unfold_in_statement unfold_lim_in_point (Some "limit") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "limit" "in" statement(constr) := + unfold_in_statement_no_error unfold_lim_in_point (Some "limit") statement. Notation "f 'is' '_continuous_' 'in' a" := (is_continuous_in _ f a) (at level 68). Notation "f 'is' 'continuous' 'in' a" := (is_continuous_in _ f a) (at level 68, only parsing). -Local Ltac2 unfold_is_continuous_in () := unfold is_continuous_in. - -Local Ltac2 unfold_is_continuous_in_in (h : ident) := unfold is_continuous_in in $h. +Local Ltac2 unfold_is_cont (statement : constr) := eval unfold is_continuous_in in $statement. -Ltac2 Notation "Expand" "the" "definition" "of" "continuous" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_continuous_in unfold_is_continuous_in_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "continuous" "in" statement(constr) := + unfold_in_statement unfold_is_cont (Some "continuous") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "continuous" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_cont (Some "continuous") statement. Close Scope R_scope. diff --git a/theories/Libs/Analysis/ContinuityDomainR.v b/theories/Libs/Analysis/ContinuityDomainR.v index e9d96a33..95dd8491 100644 --- a/theories/Libs/Analysis/ContinuityDomainR.v +++ b/theories/Libs/Analysis/ContinuityDomainR.v @@ -20,6 +20,10 @@ Require Import Coq.Reals.Reals. Require Import Notations. Require Import Tactics. +Require Import Waterproof.Automation. + +Waterproof Enable Automation RealsAndIntegers. +Waterproof Enable Automation Intuition. Open Scope R_scope. @@ -41,54 +45,117 @@ Definition limit_in_point (f : R → R) (a : R) (q : R) := Definition is_continuous_in (f : R → R) (a : R) := ((is_accumulation_point a) ∧ (limit_in_point f a (f a))) ∨ (is_isolated_point a). +Lemma every_point_in_R_acc_point_R (a : R) : + is_accumulation_point a. +Proof. + We need to show that (∀ r : R, r > 0 ⇒ ∃ x : R, 0 < | x - a | < r). + Take r : R. + Assume that (r > 0). + Choose x := (a + r/2). + It holds that (| x - a | < r). + It holds that (| x - a | > 0). + We conclude that (0 < | x - a | < r). +Qed. + +Theorem alt_char_continuity : + ∀ f : R → R, ∀ a : R, + is_continuous_in f a ⇔ ∀ ε : R, ε > 0 ⇒ ∃ δ : R, (δ > 0) ∧ (∀ x : R, 0 < | x - a | < δ ⇒ | f(x) - f(a) | < ε). +Proof. + Take h : (R → R). + Take a : R. + We show both directions. + * We need to show that (is_continuous_in(h, a) ⇒ for all ε : ℝ, + ε > 0 ⇒ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℝ, + 0 < |x - a| < δ ⇨ |h(x) - h(a)| < ε)). + Assume that (is_continuous_in h a). + Either ((is_accumulation_point a) ∧ (limit_in_point h a (h a))) or (is_isolated_point a). + + Case ((is_accumulation_point a) ∧ (limit_in_point h a (h a))). + Take ε : R. + Assume that (ε > 0). + It holds that (there exists δ1 : R, (δ1 > 0) ∧ + (for all x : R, + (0 < |x - a| < δ1) ⇒ (|(h x) - (h a)| < ε))). + Obtain such a δ1. + Choose δ := (δ1). + We show both statements. + - We conclude that (δ > 0). + - We conclude that (for all x : ℝ, + 0 < |x - a| < δ ⇨ |h(x) - h(a)| < ε). + + Case (is_isolated_point a). + It holds that (there exists ε : ℝ, ε > 0 ∧ (for all x : ℝ, |x - a| = 0 ∨ ε ≤ |x - a|)). + Obtain such an ε. + It holds that (ε > 0). + Define z := (a + ε / 2). + It holds that (|z - a| = 0 \/ ε ≤ | z - a |) (ii). + destruct ii. + - It holds that (| z - a | > 0). + Contradiction. + - It holds that (| z - a| < ε). + Contradiction. + * We need to show that ((for all ε : ℝ, + ε > 0 ⇨ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℝ, + 0 < |x - a| < δ ⇨ |h(x) - h(a)| < ε)) ⇨ is_continuous_in(h, a)). + Assume that ((for all ε : ℝ, + ε > 0 ⇨ there exists δ : ℝ, + δ > 0 ∧ (for all x : ℝ, + 0 < |x - a| < δ ⇨ |h(x) - h(a)| < ε))). + unfold is_continuous_in. + We need to show that (is_accumulation_point(a) ∧ limit_in_point(h, a, h(a)) ∨ is_isolated_point(a)). + It suffices to show that (is_accumulation_point(a) ∧ limit_in_point(h, a, h(a)) ). + We show both statements. + + By (every_point_in_R_acc_point_R) we conclude that (is_accumulation_point a). + + We conclude that (limit_in_point(h, a, h a)). +Qed. + +#[export] Hint Resolve -> alt_char_continuity : wp_reals. +#[export] Hint Resolve <- alt_char_continuity : wp_reals. (** Notations *) Notation "a 'is' 'an' '_accumulation' 'point_'" := (is_accumulation_point a) (at level 68). Notation "a 'is' 'an' 'accumulation' 'point'" := (is_accumulation_point a) (at level 68, only parsing). -Local Ltac2 unfold_is_accumulation_point () := unfold is_accumulation_point. - -Local Ltac2 unfold_is_accumulation_point_in (h : ident) := unfold is_accumulation_point in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "accumulation" "point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_accumulation_point unfold_is_accumulation_point_in cl. +Local Ltac2 unfold_acc_point (statement : constr) := eval unfold is_accumulation_point in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "accumulation" "point" "in" statement(constr) := + unfold_in_statement unfold_acc_point (Some "accumulation point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "accumulation" "point" "in" statement(constr) := + unfold_in_statement_no_error unfold_acc_point (Some "accumulation point") statement. Notation "a 'is' 'an' '_isolated' 'point_'" := (is_isolated_point a) (at level 68). Notation "a 'is' 'an' 'isolated' 'point'" := (is_isolated_point a) (at level 68, only parsing). -Local Ltac2 unfold_is_isolated_point () := unfold is_isolated_point. - -Local Ltac2 unfold_is_isolated_point_in (h : ident) := unfold is_isolated_point in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "isolated" "point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_isolated_point unfold_is_isolated_point_in cl. +Local Ltac2 unfold_isol_point (statement : constr) := eval unfold is_isolated_point in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "isolated" "point" "in" statement(constr) := + unfold_in_statement unfold_isol_point (Some "isolated point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "isolated" "point" "in" statement(constr) := + unfold_in_statement_no_error unfold_isol_point (Some "isolated point") statement. Notation "'_limit_' 'of' f 'in' a 'is' L" := (limit_in_point f a L) (at level 68). Notation "'limit' 'of' f 'in' a 'is' L" := (limit_in_point f a L) (at level 68, only parsing). -Local Ltac2 unfold_limit_in_point () := unfold limit_in_point. - -Local Ltac2 unfold_limit_in_point_in (h : ident) := unfold limit_in_point in $h. +Local Ltac2 unfold_lim_in_point (statement : constr) := eval unfold limit_in_point in $statement. -Ltac2 Notation "Expand" "the" "definition" "of" "limit" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_limit_in_point unfold_limit_in_point_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "limit" "in" statement(constr) := + unfold_in_statement unfold_lim_in_point (Some "limit") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "limit" "in" statement(constr) := + unfold_in_statement_no_error unfold_lim_in_point (Some "limit") statement. Notation "f 'is' '_continuous_' 'in' a" := (is_continuous_in f a) (at level 68). Notation "f 'is' 'continuous' 'in' a" := (is_continuous_in f a) (at level 68, only parsing). -Local Ltac2 unfold_is_continuous_in () := unfold is_continuous_in. - -Local Ltac2 unfold_is_continuous_in_in (h : ident) := unfold is_continuous_in in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "continuous" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_continuous_in unfold_is_continuous_in_in cl. +Local Ltac2 unfold_is_cont (statement : constr) := eval unfold is_continuous_in in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "continuous" "in" statement(constr) := + unfold_in_statement unfold_is_cont (Some "continuous") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "continuous" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_cont (Some "continuous") statement. -Close Scope R_scope. \ No newline at end of file +Close Scope R_scope. diff --git a/theories/Libs/Analysis/LimsupLiminfBolzano.v b/theories/Libs/Analysis/LimsupLiminfBolzano.v index 6a945907..82b4b11e 100644 --- a/theories/Libs/Analysis/LimsupLiminfBolzano.v +++ b/theories/Libs/Analysis/LimsupLiminfBolzano.v @@ -65,27 +65,26 @@ Proof. Assume that (has_lb (sequence_ub a (i))) (ii). Take m, Nn : ℕ. By exists_almost_lim_sup_aux it holds that - (∃ k : ℕ, (k ≥ Nn)%nat ∧ a k > sequence_ub a (i) Nn - 1 / (INR(m) + 1)) (iii). - Obtain n according to (iii), so for n : nat it holds that - ((n ≥ Nn)%nat ∧ a n > sequence_ub a (i) Nn - 1 / (INR(m) + 1)) (iv). + (∃ n : ℕ, (n ≥ Nn)%nat ∧ a n > sequence_ub a (i) Nn - 1 / (INR(m) + 1)). + Obtain such an n. It holds that + ((n ≥ Nn)%nat ∧ a n > sequence_ub a (i) Nn - 1 / (INR(m) + 1)) (iii). Choose k := n. We show both statements. - We need to show that (Nn ≤ k)%nat. We conclude that (Nn <= k)%nat. - We need to show that (a k > proj1_sig(_,_,lim_sup_bdd a (i) (ii)) - 1 / (m + 1)). We claim that (proj1_sig(_,_,lim_sup_bdd a (i) (ii)) ≤ sequence_ub a (i) Nn). - { Expand the definition of lim_sup_bdd. - That is, write the goal as + { We need to show that (proj1_sig(_, _, decreasing_cv (sequence_ub a (i), (Wn_decreasing a (i)), (ii))) ≤ sequence_ub a (i) Nn). Define v := (decreasing_cv (sequence_ub a (i)) (Wn_decreasing a (i)) (ii)). - Obtain l according to (v), so for l : R it holds that - (Un_cv (sequence_ub a (i)) l). + clear _defeq0. + Obtain such an l. We need to show that (l ≤ sequence_ub a (i) Nn). By Wn_decreasing it holds that (Un_decreasing (sequence_ub a (i))). By decreasing_ineq we conclude that (l <= sequence_ub a (i) Nn). } - Because (iv) both (n ≥ Nn)%nat and + Because (iii) both (n ≥ Nn)%nat and (a n > sequence_ub a i Nn - 1 / (m + 1)) hold. It holds that (proj1_sig(_, _, lim_sup_bdd a (i) (ii)) - 1 / (m + 1) ≤ a n) (v). We need to show that (proj1_sig(_, _, lim_sup_bdd a (i) (ii)) - 1 / (m + 1) < a k). @@ -116,25 +115,21 @@ Proof. Take a : (ℕ → ℝ). Assume that (has_ub a) (i). Take Nn, n : ℕ; such that (n ≥ Nn)%nat. - Expand the definition of sequence_ub. - That is, write the goal as (a n ≤ lub (fun k ↦ (a (Nn + k)%nat), maj_ss a Nn (i))). - Expand the definition of lub. - That is, write the goal as + We need to show that (a n ≤ lub (fun k ↦ (a (Nn + k)%nat), maj_ss a Nn (i))). + We need to show that (a n ≤ (let (a0, _) := ub_to_lub (fun k ↦ (a (Nn + k)%nat), maj_ss a Nn (i)) in a0)). Define ii := (ub_to_lub (fun (k : ℕ) ↦ a (Nn +k)%nat) (maj_ss a Nn (i))). - Obtain M according to (ii), so for M : R it holds that - (is_sup (EUn (fun (k : ℕ) ↦ a (Nn +k)%nat)) M) (iii). - Expand the definition of is_lub in (iii). - That is, write (iii) as (is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), M) - ∧ (for all b : ℝ, is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), b) ⇨ M ≤ b)). - Because (iii) both (is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), M)) (iv) - and (for all b : ℝ, is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), b) ⇨ M ≤ b) hold. - Expand the definition of is_upper_bound in (iv). - That is, write (iv) as (for all x : ℝ, EUn (fun k ↦ (a (Nn + k)%nat), x) ⇨ x ≤ M). + clear _defeq. + Obtain such an M. It holds that + (is_lub (EUn (fun (k : ℕ) ↦ a (Nn +k)%nat)) M). + It holds that (Raxioms.is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), M) + ∧ (for all b : ℝ, Raxioms.is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), b) ⇨ M ≤ b)) (iii). + Because (iii) both (Raxioms.is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), M)) + and (for all b : ℝ, Raxioms.is_upper_bound (EUn (fun k ↦ (a (Nn + k)%nat)), b) ⇨ M ≤ b) hold. + It holds that (for all x : ℝ, EUn (fun k ↦ (a (Nn + k)%nat), x) ⇨ x ≤ M). It holds that (Nn + (n-Nn) = n)%nat. It suffices to show that (EUn (fun (k : ℕ) ↦ (a (Nn + k)%nat)) (a n)). - Expand the definition of EUn. - That is, write the goal as (there exists i : ℕ, a n = a (Nn + i)%nat). + We need to show that (there exists i : ℕ, a n = a (Nn + i)%nat). We need to show that (∃ i : ℕ, a n = a (Nn + i)%nat). Choose k := (n - Nn)%nat. We conclude that (& a n = a (Nn + n - Nn)%nat = a (Nn + k)%nat). @@ -153,12 +148,12 @@ Proof. Define L := (proj1_sig L_with_proof). Define sequence_ub_cv_to_L := (proj2_sig L_with_proof). We claim that (∃ m : ℕ → ℕ, is_index_seq m - ∧ ∀ k : ℕ, a (m k) > L - 1 / (INR(k) + 1)) (iii). + ∧ ∀ k : ℕ, a (m k) > L - 1 / (INR(k) + 1)). { By exists_subseq_to_limsup_bdd it holds that (there exists n : ℕ ⇨ ℕ, is_index_seq n - ∧ (for all k : ℕ, a (n k) > proj1_sig(_,_,lim_sup_bdd a (i) (ii)) - 1 / (k + 1))) (iv). - Obtain m_seq according to (iv), so for m_seq : ℕ ⇨ ℕ it holds that + ∧ (for all k : ℕ, a (n k) > proj1_sig(_,_,lim_sup_bdd a (i) (ii)) - 1 / (k + 1))). + Obtain such an m_seq. It holds that (is_index_seq m_seq ∧ (for all k : ℕ, a (m_seq k) > proj1_sig(_,_,lim_sup_bdd a i ii) - 1 / (k + 1))) (v). Because (v) both (is_index_seq m_seq) and @@ -170,7 +165,7 @@ Proof. - We need to show that (for all k : ℕ, a (m k) > L - 1 / (k + 1)). By (vi) we conclude that (∀ k : ℕ, a (m k) > L - 1 / (INR(k) + 1)). } - Obtain m according to (iii), so for m : ℕ ⇨ ℕ it holds that + Obtain such an m. It holds that (is_index_seq m ∧ (for all k : ℕ, a (m k) > L - 1 / (k + 1))) (iv). Because (iv) both (is_index_seq m) (v) and (for all k : ℕ, a (m k) > L - 1 / (k + 1)) (vi) hold. @@ -214,8 +209,9 @@ Proof. By Bolzano_Weierstrass_gen it holds that (∃ (n : ℕ → ℕ), is_index_seq n ∧ Un_cv (fun (k : ℕ) ↦ a (n k)) (proj1_sig (_,_,lim_sup_bdd a (i) (iii)))) (iv). - Obtain n0 according to (iv), so for n0 : ℕ → ℕ it holds that - (is_index_seq n0 ∧ Un_cv (fun (k : ℕ) ↦ a (n0 k)) (proj1_sig (_,_,lim_sup_bdd a (i) (iii)))) (v). + Obtain such an n0. It holds that + (is_index_seq n0 ∧ + Un_cv (fun (k : ℕ) ↦ a (n0 k)) (proj1_sig (_,_,lim_sup_bdd a (i) (iii)))) (v). Choose n := n0. Choose l := (proj1_sig (_,_,lim_sup_bdd a (i) (iii))). By (v) it suffices to show that (is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), proj1_sig (_,_,lim_sup_bdd a (i) (iii)))). @@ -229,11 +225,10 @@ Proof. Take a : (ℕ → ℝ). Assume that (has_ub a) (i). Take x : ℝ. - Assume that (is_seq_acc_pt a x) (ii). - Expand the definition of is_seq_acc_pt in (ii). - That is, write (ii) as (there exists n : ℕ → ℕ, is_index_seq n + Assume that (is_seq_acc_pt a x). + It holds that (there exists n : ℕ → ℕ, is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)). - Obtain n according to (ii), so for n : ℕ → ℕ it holds that + Obtain such an n. It holds that (is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)) (iii). Because (iii) both (is_index_seq n) and (Un_cv (fun k ↦ a(n(k)), x)) hold. Take m : ℕ. @@ -243,10 +238,8 @@ Proof. It holds that (L < x). Define ε := (x - L). It holds that (ε > 0). - It holds that (∃ K : ℕ, ∀ k : ℕ, (k ≥ K)%nat ⇒ R_dist (a (n k)) x < ε) (iv). - Obtain K according to (iv), so for K : nat it holds that - (∀ k : ℕ, (k ≥ K)%nat ⇒ R_dist (a (n k)) x < ε). - Define Nn := (Nat.max K m). + It holds that (∃ K : ℕ, ∀ k : ℕ, (k ≥ K)%nat ⇒ R_dist (a (n k)) x < ε). + Obtain such a K. Define Nn := (Nat.max K m). It holds that (R_dist (a (n Nn)) x < ε). By Rabs_def2 it holds that (a (n Nn) - x < ε ∧ - ε < a (n Nn) - x) (v). Because (v) both (a (n Nn) - x < ε) and (- ε < a (n Nn) - x) hold. @@ -254,44 +247,39 @@ Proof. It holds that (a (n Nn) > L). By index_seq_grows_0 it holds that (n Nn ≥ Nn)%nat. By sequence_ub_bds it holds that (a (n Nn) ≤ L). - It holds that (~ a (n Nn) > L). Contradiction. Qed. (** ## Comparing definitions of lim sup*) -Lemma lim_sup_bdd_is_sup_seq_acc_pts : +Lemma lim_sup_bdd_is_lub_seq_acc_pts : ∀ (a : ℕ → ℝ) (i : has_ub a) (ii : has_lb (sequence_ub a (i))), - is_sup (is_seq_acc_pt a) (proj1_sig (_,_, lim_sup_bdd a (i) (ii))). + is_lub (is_seq_acc_pt a) (proj1_sig (_,_, lim_sup_bdd a (i) (ii))). Proof. Take a : (ℕ → ℝ). Assume that (has_ub a) (i). Assume that (has_lb (sequence_ub a (i))) (ii). (* TODO: fix that we refer to is_lub here. Moreover, we show both statements should work immediately. *) - Expand the definition of is_lub. - That is, write the goal as - (is_upper_bound (is_seq_acc_pt a) (proj1_sig (_,_, lim_sup_bdd a (i) (ii))) - ∧ (for all b : ℝ, is_upper_bound (is_seq_acc_pt a) b ⇨ proj1_sig (_,_,lim_sup_bdd a (i) (ii)) ≤ b)). + We need to show that + (Raxioms.is_upper_bound (is_seq_acc_pt a) (proj1_sig (_,_, lim_sup_bdd a (i) (ii))) + ∧ (for all b : ℝ, Raxioms.is_upper_bound (is_seq_acc_pt a) b ⇨ proj1_sig (_,_,lim_sup_bdd a (i) (ii)) ≤ b)). We show both statements. - - We need to show that (is_upper_bound (is_seq_acc_pt a) (proj1_sig (_,_,lim_sup_bdd a (i) (ii)))). - Expand the definition of is_upper_bound. - That is, write the goal as (for all x : ℝ, is_seq_acc_pt a x ⇨ x ≤ proj1_sig (_,_,lim_sup_bdd a (i) (ii))). + - We need to show that (Raxioms.is_upper_bound (is_seq_acc_pt a) (proj1_sig (_,_,lim_sup_bdd a (i) (ii)))). + We need to show that (for all x : ℝ, is_seq_acc_pt a x ⇨ x ≤ proj1_sig (_,_,lim_sup_bdd a (i) (ii))). Take x : ℝ. Assume that (is_seq_acc_pt a x). By acc_pt_bds_seq_ub it holds that (∀ m : ℕ, x ≤ sequence_ub a (i) m). Define iii := (lim_sup_bdd a (i) (ii)). - Obtain L according to (iii), so for L : R it holds that (Un_cv (sequence_ub a i) L). - simpl. + clear _defeq. + Obtain such an L. simpl. By (low_bd_seq_is_low_bd_lim (sequence_ub a (i))) it holds that (L ≥ x). We conclude that (x ≤ L). - - We need to show that (for all b : ℝ, is_upper_bound (is_seq_acc_pt a) b + - We need to show that (for all b : ℝ, Raxioms.is_upper_bound (is_seq_acc_pt a) b ⇨ proj1_sig (_,_,lim_sup_bdd a (i) (ii)) ≤ b). - Take b : ℝ; such that (is_upper_bound (is_seq_acc_pt a) b) (iii). - Expand the definition of is_upper_bound in (iii). - That is, write (iii) as (for all x : ℝ, is_seq_acc_pt a x ⇨ x ≤ b). - Expand the definition of is_seq_acc_pt in (iii). - That is, write (iii) as (for all x : ℝ, - (there exists n : ℕ ⇨ ℕ, is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)) ⇨ x ≤ b). + Take b : ℝ; such that (Raxioms.is_upper_bound (is_seq_acc_pt a) b). + It holds that (for all x : ℝ, is_seq_acc_pt a x ⇨ x ≤ b). + It holds that (for all x : ℝ, + (there exists n : ℕ ⇨ ℕ, is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)) ⇨ x ≤ b) (iii). By (iii) it suffices to show that (there exists n : ℕ ⇨ ℕ, is_index_seq n ∧ Un_cv (fun k ↦ a(n(k))) (proj1_sig (_,_,lim_sup_bdd a (i) (ii)))). By Bolzano_Weierstrass_gen we conclude that (there exists n : ℕ ⇨ ℕ, is_index_seq n diff --git a/theories/Libs/Analysis/MetricSpaces.v b/theories/Libs/Analysis/MetricSpaces.v index ab180a4e..6c2b9df2 100644 --- a/theories/Libs/Analysis/MetricSpaces.v +++ b/theories/Libs/Analysis/MetricSpaces.v @@ -20,10 +20,10 @@ Require Import Coq.Reals.Reals. Require Import Reals.ROrderedType. Require Import micromega.Lra. +Require Import Tactics. Require Import Automation. Require Import Libs.Reals. Require Import Notations. -Require Import Tactics. Waterproof Enable Automation RealsAndIntegers. @@ -63,7 +63,7 @@ Defined. End Definitions. -(** ** Expample : a discrete metric on the real line *) +(** ** Example : a discrete metric on the real line *) Definition d_discrete_R : ℝ → ℝ → ℝ := fun (x y : ℝ) => if Reqb x y then 0 else 3. @@ -72,28 +72,25 @@ Lemma d'_eq_0 : forall x y : ℝ, d_discrete_R x y = 0 -> (Reqb x y) = true. Proof. Take x, y : ℝ. -Assume that (d_discrete_R x y = 0) (i). +Assume that (d_discrete_R x y = 0). Either (x = y) or (x ≠ y). + Case (x = y). By Req_true we conclude that (Reqb x y = true). + Case (x ≠ y). - Expand the definition of d_discrete_R in (i). - That is, write (i) as ( (if Reqb x y then 0 else 3) = 0). - rewrite (Req_false x y n) in i. - It holds that (3 ≠ 0). + It holds that ((if Reqb(x, y) then 0 else 3) = 0) (i). + rewrite (Req_false x y H) in i. Contradiction. Qed. Lemma d'_eq_3 : forall x y : ℝ, d_discrete_R x y = 3 -> (Reqb x y) = false. Proof. Take x, y : ℝ. -Assume that (d_discrete_R x y = 3) (i). -Expand the definition of d_discrete_R in (i). -That is, write (i) as ( (if Reqb x y then 0 else 3) = 3). +Assume that (d_discrete_R x y = 3). +It holds that ( (if Reqb x y then 0 else 3) = 3) (i). Either (x = y) or (x ≠ y). + Case (x = y). - rewrite (Req_true x y e) in i. + rewrite (Req_true x y H) in i. It holds that (0 ≠ 3). Contradiction. + Case (x ≠ y). diff --git a/theories/Libs/Analysis/OpenAndClosed.v b/theories/Libs/Analysis/OpenAndClosed.v index 33b3c64e..a4058e4f 100644 --- a/theories/Libs/Analysis/OpenAndClosed.v +++ b/theories/Libs/Analysis/OpenAndClosed.v @@ -44,64 +44,66 @@ Definition is_closed (A : R -> Prop) := is_open (complement A). (** Notations *) Notation "B( p , r )" := (open_ball p r) (at level 68, format "B( p , r )"). -Local Ltac2 unfold_open_ball () := unfold open_ball, pred. +Local Ltac2 unfold_open_ball (statement : constr) := eval unfold open_ball, pred in $statement. -Local Ltac2 unfold_open_ball_in (h : ident) := unfold open_ball, pred in $h. +Ltac2 Notation "Expand" "the" "definition" "of" "B" "in" statement(constr) := + unfold_in_statement unfold_open_ball (Some "B") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "B" "in" statement(constr) := + unfold_in_statement_no_error unfold_open_ball (Some "B") statement. -Ltac2 Notation "Expand" "the" "definition" "of" "B" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_open_ball unfold_open_ball_in cl. - -Ltac2 Notation "Expand" "the" "definition" "of" "open" "ball" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_open_ball unfold_open_ball_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "open" "ball" "in" statement(constr) := + unfold_in_statement unfold_open_ball (Some "open ball ") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "open" "ball" "in" statement(constr) := + unfold_in_statement_no_error unfold_open_ball (Some "open ball ") statement. Notation "a 'is' 'an' '_interior' 'point_' 'of' A" := (is_interior_point a A) (at level 68). Notation "a 'is' 'an' 'interior' 'point' 'of' A" := (is_interior_point a A) (at level 68, only parsing). -Local Ltac2 unfold_is_interior_point () := unfold is_interior_point. - -Local Ltac2 unfold_is_interior_point_in (h : ident) := unfold is_interior_point in $h. +Local Ltac2 unfold_is_interior_point (statement : constr) := eval unfold is_interior_point in $statement. -Ltac2 Notation "Expand" "the" "definition" "of" "interior" "point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_interior_point unfold_is_interior_point_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "interior" "point" "in" statement(constr) := + unfold_in_statement unfold_is_interior_point (Some "interior point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "interior" "point" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_interior_point (Some "interior point") statement. Notation "A 'is' '_open_'" := (is_open A) (at level 68). Notation "A 'is' 'open'" := (is_open A) (at level 68, only parsing). -Local Ltac2 unfold_is_open () := unfold is_open. - -Local Ltac2 unfold_is_open_in (h : ident) := unfold is_open in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "open" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_open unfold_is_open_in cl. +Local Ltac2 unfold_is_open (statement : constr) := eval unfold is_open in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "open" "in" statement(constr) := + unfold_in_statement unfold_is_open (Some "open") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "open" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_open (Some "open") statement. Notation "'ℝ\' A" := (complement A) (at level 20, format "'ℝ\' A"). Notation "'ℝ' '\' A" := (complement A) (at level 20, only parsing). -Local Ltac2 unfold_complement () := unfold complement, pred. +Local Ltac2 unfold_complement (statement : constr) := eval unfold complement, pred in $statement. -Local Ltac2 unfold_complement_in (h : ident) := unfold complement, pred in $h. +Ltac2 Notation "Expand" "the" "definition" "of" "ℝ\" "in" statement(constr) := + unfold_in_statement unfold_complement (Some "ℝ\") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "ℝ\" "in" statement(constr) := + unfold_in_statement_no_error unfold_complement (Some "ℝ\") statement. -Ltac2 Notation "Expand" "the" "definition" "of" "ℝ\" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_complement unfold_complement_in cl. - -Ltac2 Notation "Expand" "the" "definition" "of" "complement" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_complement unfold_complement_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "complement" "in" statement(constr) := + unfold_in_statement unfold_complement (Some "complement") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "complement" "in" statement(constr) := + unfold_in_statement_no_error unfold_complement (Some "complement") statement. Notation "A 'is' '_closed_'" := (is_closed A) (at level 68). Notation "A 'is' 'closed'" := (is_closed A) (at level 68, only parsing). -Local Ltac2 unfold_is_closed () := unfold is_closed. - -Local Ltac2 unfold_is_closed_in (h : ident) := unfold is_closed in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "closed" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_closed unfold_is_closed_in cl. +Local Ltac2 unfold_is_closed (statement : constr) := eval unfold is_closed in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "closed" "in" statement(constr) := + unfold_in_statement unfold_is_closed (Some "closed") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "closed" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_closed (Some "closed") statement. (** Hints *) Lemma zero_in_interval_closed_zero_open_one : (0 : [0,1)). diff --git a/theories/Libs/Analysis/Sequences.v b/theories/Libs/Analysis/Sequences.v index e17f9d86..53c1088b 100644 --- a/theories/Libs/Analysis/Sequences.v +++ b/theories/Libs/Analysis/Sequences.v @@ -22,13 +22,12 @@ Require Import Classical. Require Import Classical_Pred_Type. Require Import ClassicalChoice. +Require Import Tactics. Require Import Automation. Require Import Libs.Reals. Require Import Notations. -Require Import Tactics. -#[export] Hint Resolve Rabs_Rabsolu : wp_reals. -#[export] Hint Resolve Rabs_minus_sym : wp_reals. + #[export] Hint Resolve Rmult_lt_0_compat : wp_reals. #[export] Hint Resolve Rinv_lt_contravar : wp_reals. @@ -90,21 +89,17 @@ Proof. Either (x <= 0) or (0 < x). - Case (x <= 0). Choose n := 1%nat. - We claim that (INR 1 > INR 0). - { Expand the definition of INR. - That is, write the goal as ( 1 > 0 ). - We conclude that (1 > 0). - } - It holds that (x <= INR 0). - We conclude that (n > x). + It holds that (INR n > INR 0). + It holds that (x <= 0%nat). + We conclude that (INR n > x). - Case (0 < x). By archimed it holds that (IZR( up x) > x ∧ IZR( up x ) - x ≤ 1). It holds that (IZR( up x ) > x). It holds that (0 < IZR( up x )). By lt_0_IZR it holds that (0 < up x)%Z. It holds that (0 <= up x)%Z. - By IZN it holds that (∃ k : ℕ, up x = Z.of_nat k) (i). - Obtain k according to (i), so for k : nat it holds that (up x = Z.of_nat k) (ii). + By IZN it holds that (∃ k : ℕ, up x = Z.of_nat k). + Obtain such a k. It holds that (up x = Z.of_nat k) (ii). Choose n := k. We need to show that (INR k > x). By INR_IZR_INZ it holds that (INR k = IZR (Z.of_nat k)). @@ -129,11 +124,11 @@ Proof. for all n : ℕ, (n ≥ N)%nat ⇨ |b n - l| < ε ). Take ε : ℝ; such that (ε > 0). It holds that - (there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat ⇨ |a n - l| < ε) (ii). - Obtain N1 according to (ii), so for N1 : nat it holds that - (for all n : ℕ, (n ≥ N1)%nat ⇨ |a n - l| < ε). - Obtain K according to (i), so for K : nat it holds that - (for all n : ℕ, (n ≥ K)%nat ⇨ a n = b n). + (there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat ⇨ |a n - l| < ε). + Obtain such an N1. + By (i) it holds that + (there exists K : nat, for all n : ℕ, (n ≥ K)%nat ⇨ a n = b n). + Obtain such a K. Choose M := (Nat.max N1 K). Take n : ℕ; such that (n ≥ M)%nat. It holds that (b n = a n). @@ -189,20 +184,15 @@ Definition d := fun (n : ℕ) ↦ 1 / (n + 1). Lemma lim_d_0 : Un_cv d 0. Proof. - Expand the definition of d. - That is, write the goal as (Un_cv(fun n ↦ (1 / (n + 1)), 0)). - Expand the definition of Un_cv. - That is, write the goal as (for all eps : ℝ, eps > 0 + We need to show that (Un_cv (fun n => 1 / (n + 1)) 0). + We need to show that (for all eps : ℝ, eps > 0 ⇨ there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat ⇨ | 1 / (n + 1) - 0 | < eps ). Take ε : ℝ; such that (ε > 0). - By archimed_mod it holds that (there exists n : ℕ, n > / ε) (i). - Obtain n1 according to (i), so for n : nat it holds that (n > /ε). - Choose N := n1. + By archimed_mod it holds that (there exists n : ℕ, n > / ε). + Obtain such an n1. Choose N := n1. Take n : ℕ; such that (n ≥ n1)%nat. - Expand the definition of Rabs. - That is, write the goal as (|1 / (n + 1) - 0| < ε). - By Rabs_def1 it suffices to show that (-ε < 1 / (n + 1) - 0 < ε). + It suffices to show that (-ε < 1 / (n + 1) - 0 < ε). We show both (-ε < 1 / (n + 1) - 0) and (1 / (n + 1) - 0 < ε). - It holds that (0 < n + 1). (* n + 1 > 0 is difficult?*) We conclude that (& -ε < 0 < / (n + 1) = 1 / (n + 1) - 0). @@ -217,14 +207,10 @@ Lemma min_1_over_n_plus_1_to_0 : Proof. By lim_d_0 it holds that (Un_cv d 0). By (CV_opp) it holds that (Un_cv (opp_seq d) (-0)) (i). - Expand the definition of opp_seq in (i). - That is, write (i) as ( Un_cv (fun n ↦ -d(n), -0)). - Expand the definition of d in (i). - That is, write (i) as ( Un_cv (fun n ↦ -(1 / (n + 1)), -0)). + It holds that ( Un_cv (fun n ↦ -d(n), -0)). + It holds that ( Un_cv (fun n ↦ -(1 / (n + 1)), -0)). It holds that (0 = -0). - (* TODO: make transport automatic *) - By (eq_ind_r(_, _, fun x => Un_cv (fun n ↦ -(1 / (n + 1)), x), (i))) - it suffices to show that (Un_cv (fun n ↦ -(1 / (n + 1)), -0)). + It suffices to show that (Un_cv (fun n ↦ -(1 / (n + 1)), -0)). By (i) we conclude that (Un_cv (fun n ↦ -(1 / (n + 1)), -0)). Qed. @@ -241,33 +227,25 @@ Proof. Assume that (c ⟶ l). To show: (∀ ε : ℝ, ε > 0 ⇒ ∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ |b n - l| < ε). Take ε : ℝ; such that (ε > 0). - It holds that (∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ |a n - l| < ε) (i). - It holds that (∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ |c n - l| < ε) (ii). - Obtain Na according to (i), so for Na : nat it holds that - (∀ n : ℕ, (n ≥ Na)%nat ⇒ |a n - l| < ε). - Obtain Nc according to (ii), so for Nc : nat it holds that - (∀ n : ℕ, (n ≥ Nc)%nat ⇒ |c n - l| < ε). + It holds that (∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ |a n - l| < ε). + Obtain such an Na. + It holds that (∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ |c n - l| < ε). + Obtain such an Nc. Choose Nn := (Nat.max Na Nc). Take n : ℕ; such that (n ≥ Nn)%nat. We claim that (-ε < a n - l). { It holds that (n ≥ Na)%nat. It holds that (R_dist (a n) l < ε) (iii). - Expand the definition of Rabs in (iii). - That is, write (iii) as (|a(n) - l| < ε). By Rabs_def2 it holds that (a n - l < ε /\ -ε < a n - l). We conclude that (-ε < a n - l). } We claim that (c n - l < ε). { It holds that (n ≥ Nc)%nat. It holds that (R_dist (c n) l < ε) (iii). - Expand the definition of Rabs in (iii). - That is, write (iii) as (|c(n) - l| < ε). By Rabs_def2 it holds that (c n - l < ε /\ -ε < c n - l). We conclude that (c n - l < ε). } - Expand the definition of Rabs. - That is, write the goal as ((if Rcase_abs(b(n) - l) then - (b(n) - l) else b(n) - l) < ε). - By Rabs_def1 it suffices to show that (-ε < b n - l < ε). + It suffices to show that (-ε < b n - l < ε). It holds that (a n ≤ b n ∧ b n ≤ c n). We show both (- ε < b n - l) and ( b n - l < ε). - We conclude that (& - ε < a n - l <= b n - l). @@ -292,16 +270,12 @@ Proof. - Case (M < L). Define ε := (L-M). It holds that (ε > 0). - Expand the definition of Un_cv in (i). - That is, write (i) as (for all eps : ℝ, eps > 0 + It holds that (for all eps : ℝ, eps > 0 ⇨ there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat ⇨ | a n - L | < eps). - It holds that (∃ N : ℕ, ∀n : ℕ, (n ≥ N)%nat ⇒ R_dist (a n) L < ε) (iii). - Obtain Nn according to (iii), so for Nn : nat it holds that - (∀n : ℕ, (n ≥ Nn)%nat ⇒ R_dist (a n) L < ε). - It holds that (R_dist (a Nn) L < ε) (iv). - Expand the definition of Rabs in (iv). - That is, write (iv) as (|a(Nn) - L| < ε). + It holds that (∃ N : ℕ, ∀n : ℕ, (n ≥ N)%nat ⇒ R_dist (a n) L < ε). + Obtain such an Nn. + It holds that (|a(Nn) - L| < ε). By Rabs_def2 it holds that (a Nn - L < ε ∧ (- ε < a Nn - L)). It holds that (- ε < a Nn - L). It holds that (a Nn ≤ M). @@ -321,8 +295,7 @@ Proof. Assume that (∀ n : ℕ, a n ≥ M). Define b := (opp_seq a). Assume that (Un_cv a L). - Expand the definition of opp_seq in (b). - That is, write (b) as ( ℕ ⇨ ℝ ). (*TODO *) + It holds that (b = (fun n => - a n)). By CV_opp it holds that (Un_cv b (-L)). We claim that (-L ≤ -M). { By upp_bd_seq_is_upp_bd_lim it suffices to show that @@ -344,42 +317,33 @@ Proof. Take b : (ℕ → ℝ). Take m : ℝ. Take l : ℝ. - Assume that (Un_cv a m) (i) and (Un_cv b l) (ii). + Assume that (Un_cv a m) and (Un_cv b l). Assume that (∀ n : ℕ, a n ≤ b n). We argue by contradiction. Assume that (~ m <= l). It holds that (l < m). Define ε := ((m - l)/2). It holds that (ε > 0). - Expand the definition of Un_cv in (ii). - That is, write (ii) as (for all eps : ℝ, eps > 0 + It holds that (for all eps : ℝ, eps > 0 + ⇨ there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat + ⇨ | a n - m | < eps). + It holds that (for all eps : ℝ, eps > 0 ⇨ there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat ⇨ | b n - l | < eps). - Expand the definition of Un_cv in (i). - That is, write (i) as (for all eps : ℝ, eps > 0 - ⇨ there exists N : ℕ, for all n : ℕ, (n ≥ N)%nat - ⇨ | a n - m | < eps ). - It holds that (∃ N1 : ℕ, ∀ n : ℕ, (n ≥ N1)%nat ⇒ R_dist (b n) l < ε) (iii). - It holds that (∃ N2 : ℕ, ∀ n : ℕ, (n ≥ N2)%nat ⇒ R_dist (a n) m < ε) (iv). - Obtain N1 according to (iii), so for N1 : nat it holds that - (∀ n : ℕ, (n ≥ N1)%nat ⇒ R_dist (b n) l < ε). - Obtain N2 according to (iv), so for N2 : nat it holds that - (∀ n : ℕ, (n ≥ N2)%nat ⇒ R_dist (a n) m < ε). + It holds that (∃ N1 : ℕ, ∀ n : ℕ, (n ≥ N1)%nat ⇒ R_dist (a n) m < ε). + Obtain such an N1. + It holds that (∃ N2 : ℕ, ∀ n : ℕ, (n ≥ N2)%nat ⇒ R_dist (b n) l < ε). + Obtain such an N2. Define N3 := (Nat.max N1 N2). We claim that (b N3 < a N3). - { It holds that (R_dist (b N3) l < ε) (v). - It holds that (R_dist (a N3) m < ε) (vi). - Expand the definition of Rabs in (v). - That is, write (v) as (|b(N3) - l| < ε). - Expand the definition of Rabs in (vi). - That is, write (vi) as ( | a N3 - m | < ε ). + { It holds that (|b(N3) - l| < ε). + It holds that (|a(N3) - m| < ε). By Rabs_def2 it holds that (a N3 - m < ε ∧ - ε < a N3 - m). By Rabs_def2 it holds that (b N3 - l < ε ∧ - ε < b N3 - l). We conclude that (& b N3 < l + ε = l + (m - l)/2 = m - (m - l)/2 = m - ε < a N3). } It holds that (a N3 <= b N3). - It holds that (~ a N3 <= b N3). Contradiction. Qed. @@ -390,10 +354,11 @@ Definition is_bounded (a : ℕ → ℝ) := |a n - q| ≤ M. Notation "a 'is' '_bounded_'" := (is_bounded a) (at level 20). Notation "a 'is' 'bounded'" := (is_bounded a) (at level 20, only parsing). -Local Ltac2 unfold_is_bounded () := unfold is_bounded. -Local Ltac2 unfold_is_bounded_in (h : ident) := unfold is_bounded in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "bounded" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_bounded unfold_is_bounded_in cl. +Local Ltac2 unfold_is_bounded (statement : constr) := eval unfold is_bounded in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "in" statement(constr) := + unfold_in_statement unfold_is_bounded (Some "bounded") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "bounded" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_bounded (Some "bounded") statement. Definition is_bounded_equivalent (a : ℕ → ℝ) := ∃ M : ℝ, M > 0 ∧ @@ -407,13 +372,15 @@ Proof. Take a : (ℕ → ℝ). We show both directions. - We need to show that (is_bounded a ⇨ is_bounded_equivalent a). - Assume that (is_bounded a) (i). - Obtain q according to (i), so for q : R it holds that - (there exists M : R, M > 0 ∧ (for all n : ℕ, | a n - q | ≤ M)) (ii). - Obtain M1 according to (ii), so for M1 : R it holds that - (M1 > 0 ∧ (for all n : ℕ, | a n - q | ≤ M1)) (iii). - Because (iii) both (M1 > 0) and - (for all n : ℕ, | a n - q | ≤ M1) hold. + Assume that (is_bounded a). + It holds that (there exists q : R, + there exists M1 : R, M1 > 0 ∧ (for all n : ℕ, | a n - q | ≤ M1)). + Obtain such a q. + It holds that (there exists M1 : R, M1 > 0 ∧ (for all n : ℕ, | a n - q | ≤ M1)). + Obtain such an M1. + It holds that (M1 > 0 ∧ (for all n : ℕ, | a n - q | ≤ M1)) (i). + Because (i) both (M1 > 0) and + (for all n : ℕ, | a n - q | ≤ M1). We need to show that ( there exists M : ℝ , M > 0 ∧ (for all n : ℕ, @@ -435,7 +402,11 @@ We show both directions. - We need to show that ( is_bounded_equivalent a ⇨ is_bounded a). - Assume that (there exists M : ℝ, M > 0 ∧ ∀ n : ℕ, |a n| ≤ M) (i). + Assume that (there exists M1 : ℝ, M1 > 0 ∧ ∀ n : ℕ, |a n| ≤ M1). + Obtain such an M1. It holds that + (M1 > 0 ∧ ∀ n : ℕ, |a n| ≤ M1) (i). + Because (i) both (M1 > 0) and + (for all n : ℕ, | a n | ≤ M1) hold. (* Expand the definition of is_bounded. *) We need to show that ( there exists q M : ℝ , @@ -443,10 +414,6 @@ M > 0 ∧ (for all n : ℕ, | a n - q | ≤ M) ). Choose q := 0. - Obtain M1 according to (i), so for M1 : R it holds that - (M1 > 0 ∧ (for all n : ℕ, | a n | ≤ M1)) (ii). - Because (ii) both (M1 > 0) and - (for all n : ℕ, | a n | ≤ M1) hold. Choose M := M1. We show both statements. + We need to show that (M > 0). @@ -463,52 +430,62 @@ Definition is_bounded_above (a : ℕ → ℝ) := ∃ M : ℝ, ∀ n : ℕ, a(n) ≤ M. Notation "a 'is' '_bounded' 'above_'" := (is_bounded_above a) (at level 20). Notation "a 'is' 'bounded' 'above'" := (is_bounded_above a) (at level 20, only parsing). -Local Ltac2 unfold_is_bounded_above () := unfold is_bounded_above. -Local Ltac2 unfold_is_bounded_above_in (h : ident) := unfold is_bounded_above in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "above" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_bounded_above unfold_is_bounded_above_in cl. +Local Ltac2 unfold_is_bounded_above (statement : constr) := eval unfold is_bounded_above in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "above" "in" statement(constr) := + unfold_in_statement unfold_is_bounded_above (Some "bounded above") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "bounded" "above" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_bounded_above (Some "bounded above") statement. Definition is_bounded_below (a : ℕ → ℝ) := ∃ m : ℝ, ∀ n : ℕ, m ≤ a(n). Notation "a 'is' '_bounded' 'below_'" := (is_bounded_below a) (at level 20). Notation "a 'is' 'bounded' 'below'" := (is_bounded_below a) (at level 20, only parsing). -Local Ltac2 unfold_is_bounded_below () := unfold is_bounded_below. -Local Ltac2 unfold_is_bounded_below_in (h : ident) := unfold is_bounded_below in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "below" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_bounded_below unfold_is_bounded_below_in cl. - +Local Ltac2 unfold_is_bounded_below (statement : constr) := eval unfold is_bounded_below in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "below" "in" statement(constr) := + unfold_in_statement unfold_is_bounded_below (Some "bounded below") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "bounded" "below" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_bounded_below (Some "bounded below") statement. (** Convergence to +∞ and -∞. *) Definition diverges_to_plus_infinity (a : ℕ → ℝ) := ∀ M : ℝ, - ∃ N : ℕ, - ∀ n : ℕ, (n ≥ N)%nat ⇒ - a(n) ≥ M. + ∃ N1 : ℕ, + ∀ n : ℕ, (n ≥ N1)%nat ⇒ + a(n) > M. Notation "a ⟶ ∞" := (diverges_to_plus_infinity a) (at level 20). Notation "a '_diverges' 'to' '∞_'" := (diverges_to_plus_infinity a) (at level 20). Notation "a 'diverges' 'to' '∞'" := (diverges_to_plus_infinity a) (at level 20, only parsing). -Local Ltac2 unfold_diverge_plus_infty () := unfold diverges_to_plus_infinity. -Local Ltac2 unfold_diverge_plus_infty_in (h : ident) := unfold diverges_to_plus_infinity in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "⟶" "∞" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_diverge_plus_infty unfold_diverge_plus_infty_in cl. -Ltac2 Notation "Expand" "the" "definition" "of" "diverges" "to" "∞" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_diverge_plus_infty unfold_diverge_plus_infty_in cl. +Local Ltac2 unfold_diverge_plus_infty (statement : constr) := + eval unfold diverges_to_plus_infinity in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "⟶" "∞" "in" statement(constr) := + unfold_in_statement unfold_diverge_plus_infty (Some "⟶ ∞") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "⟶" "∞" "in" statement(constr) := + unfold_in_statement_no_error unfold_diverge_plus_infty (Some "⟶ ∞") statement. +Ltac2 Notation "Expand" "the" "definition" "of" "diverges" "to" "∞" "in" statement(constr) := + unfold_in_statement unfold_diverge_plus_infty (Some "diverges to ∞") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "diverges" "to" "∞" "in" statement(constr) := + unfold_in_statement_no_error unfold_diverge_plus_infty (Some "diverges to ∞") statement. + Definition diverges_to_minus_infinity (a : ℕ → ℝ) := ∀ M : ℝ, - ∃ N : ℕ, - ∀ n : ℕ, (n ≥ N)%nat ⇒ - a(n) ≤ M. + ∃ N1 : ℕ, + ∀ n : ℕ, (n ≥ N1)%nat ⇒ + a(n) < M. Notation "a ⟶ -∞" := (diverges_to_minus_infinity a) (at level 20). Notation "a '_diverges' 'to' '-∞_'" := (diverges_to_minus_infinity a) (at level 20). Notation "a 'diverges' 'to' '-∞'" := (diverges_to_minus_infinity a) (at level 20, only parsing). -Local Ltac2 unfold_diverge_minus_infty () := unfold diverges_to_minus_infinity. -Local Ltac2 unfold_diverge_minus_infty_in (h : ident) := unfold diverges_to_minus_infinity in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "⟶" "-∞" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_diverge_minus_infty unfold_diverge_minus_infty_in cl. -Ltac2 Notation "Expand" "the" "definition" "of" "diverges" "to" "-∞" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_diverge_minus_infty unfold_diverge_minus_infty_in cl. +Local Ltac2 unfold_diverge_minus_infty (statement : constr) := + eval unfold diverges_to_minus_infinity in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "⟶" "-∞" "in" statement(constr) := + unfold_in_statement unfold_diverge_minus_infty (Some "⟶ -∞") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "⟶" "-∞" "in" statement(constr) := + unfold_in_statement_no_error unfold_diverge_minus_infty (Some "⟶ -∞") statement. +Ltac2 Notation "Expand" "the" "definition" "of" "diverges" "to" "-∞" "in" statement(constr) := + unfold_in_statement unfold_diverge_minus_infty (Some "diverges to -∞") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "diverges" "to" "-∞" "in" statement(constr) := + unfold_in_statement_no_error unfold_diverge_minus_infty (Some "diverges to -∞") statement. Close Scope R_scope. diff --git a/theories/Libs/Analysis/SequencesMetric.v b/theories/Libs/Analysis/SequencesMetric.v index 3970350e..b1091e8f 100644 --- a/theories/Libs/Analysis/SequencesMetric.v +++ b/theories/Libs/Analysis/SequencesMetric.v @@ -33,7 +33,7 @@ Definition convergence {M : Metric_Space} (a : ℕ → Base M) (c : Base M) := ∀ ε : ℝ, ε > 0 ⇒ - ∃ N : ℕ, ∀ n : ℕ, (n ≥ N)%nat ⇒ + ∃ N1 : ℕ, ∀ n : ℕ, (n ≥ N1)%nat ⇒ dist M (a n) c < ε. Definition bounded {X : Metric_Space} (a : ℕ → Base X) := @@ -43,18 +43,21 @@ Definition bounded {X : Metric_Space} (a : ℕ → Base X) := Declare Scope metric_scope. Notation "a ⟶ c" := (convergence a c) (at level 20) : metric_scope. -Local Ltac2 unfold_convergence () := unfold convergence. -Local Ltac2 unfold_convergence_in (h : ident) := unfold convergence in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "⟶" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_convergence unfold_convergence_in cl. +Local Ltac2 unfold_convergence (statement : constr) := eval unfold convergence in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "⟶" "in" statement(constr) := + unfold_in_statement unfold_convergence (Some "⟶") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "⟶" "in" statement(constr) := + unfold_in_statement_no_error unfold_convergence (Some "⟶") statement. (* With -->, waterproof complains, giving the following error: Command not supported (No proof-editing in progress)*) Notation "a '_converges' 'to_' p" := (convergence a p) (at level 68) : metric_scope. Notation "a 'converges' 'to' p" := (convergence a p) (at level 68, only parsing) : metric_scope. -Ltac2 Notation "Expand" "the" "definition" "of" "converges" "to" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_convergence unfold_convergence_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "converges" "to" "in" statement(constr) := + unfold_in_statement unfold_convergence (Some "converges to") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "converges" "to" "in" statement(constr) := + unfold_in_statement_no_error unfold_convergence (Some "converges to") statement. (* Index shift*) Lemma relation_shift {X : Metric_Space} (a : nat -> Base X) (k : nat) (n : nat) (n_ge_k : (n ≥ k)%nat) : diff --git a/theories/Libs/Analysis/SequentialAccumulationPoints.v b/theories/Libs/Analysis/SequentialAccumulationPoints.v index 3032dc41..e37f1c9d 100644 --- a/theories/Libs/Analysis/SequentialAccumulationPoints.v +++ b/theories/Libs/Analysis/SequentialAccumulationPoints.v @@ -40,30 +40,24 @@ Lemma seq_bdd_seq_acc_pts_bdd : Proof. Take a : (ℕ → ℝ). Assume that (has_ub a) (i). - Expand the definition of bound. - That is, write the goal as + We need to show that (there exists m : ℝ, is_upper_bound (is_seq_acc_pt a) m). - Expand the definition of is_upper_bound. - That is, write the goal as (there exists m : ℝ, + We need to show that (there exists m : ℝ, for all x : ℝ, is_seq_acc_pt a x ⇨ x ≤ m). - Obtain M according to (i), so for M : R it holds that - (is_upper_bound (EUn a) M) (ii). + By (i) it holds that (there exists M : R, is_upper_bound (EUn a) M). + Obtain such an M. It holds that (is_upper_bound (EUn a) M). Choose m := M. Take x : ℝ. - Assume that (is_seq_acc_pt a x) (iii). - Expand the definition of is_seq_acc_pt in (iii). - That is, write (iii) as (there exists n : ℕ ⇨ ℕ, + Assume that (is_seq_acc_pt a x). + It holds that (there exists n : ℕ ⇨ ℕ, is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)). - Obtain n according to (iii), so for n : ℕ → ℕ it holds that - (is_index_seq n ∧ Un_cv (fun k ↦ (a(n(k))), x)) (iv). + Obtain such an n. It holds that (is_index_seq n ∧ Un_cv (fun k ↦ a(n(k)), x)) (iv). Because (iv) both (is_index_seq n) and (Un_cv (fun k ↦ (a(n(k))), x)) hold. We need to show that (x ≤ M). - By upp_bd_seq_is_upp_bd_lim it suffices to show that (for all k : nat, (a (n k) <= M)). + By (upp_bd_seq_is_upp_bd_lim (fun k => a (n k))) it suffices to show that (for all k : nat, (a (n k) <= M)). We claim that (for all k : ℕ, (a k) ≤ M) (v). - { Expand the definition of is_upper_bound in (ii). - That is, write (ii) as (for all x0 : ℝ, EUn a x0 ⇨ x0 ≤ M). - Expand the definition of EUn in (ii). - That is, write (ii) as (for all x0 : ℝ, + { It holds that (for all x0 : ℝ, EUn a x0 ⇨ x0 ≤ M). + It holds that (for all x0 : ℝ, (there exists k : ℕ, x0 = a k) ⇨ x0 ≤ M). Take k : ℕ. It suffices to show that (there exists k0 : nat, a k = a k0). diff --git a/theories/Libs/Analysis/Series.v b/theories/Libs/Analysis/Series.v index ed15c94c..e37f6c8b 100644 --- a/theories/Libs/Analysis/Series.v +++ b/theories/Libs/Analysis/Series.v @@ -60,8 +60,6 @@ Lemma partial_sums_pos_incr : Un_growing (partial_sums a). Proof. Take a : (ℕ → ℝ); such that (∀ n : ℕ, a n ≥ 0). - Expand the definition of Un_growing. - That is, write the goal as (for all n : ℕ, partial_sums a n ≤ partial_sums a (S n)). We need to show that (for all n : ℕ, partial_sums a n ≤ partial_sums a (S n)). Take n : ℕ. It holds that (a (S n) ≥ 0). diff --git a/theories/Libs/Analysis/StrongInductionIndexSequence.v b/theories/Libs/Analysis/StrongInductionIndexSequence.v new file mode 100644 index 00000000..d3358648 --- /dev/null +++ b/theories/Libs/Analysis/StrongInductionIndexSequence.v @@ -0,0 +1,328 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Lia. +Require Import Arith. +Require Import Arith.Compare. +Require Import ClassicalChoice. +Require Import ChoiceFacts. + +Require Export Libs.Analysis.SubsequencesMetric. + + +Section Construction. + +Definition dep_only_on_start {A : Type} (P : nat -> (nat -> A) -> Prop) + := forall (k : nat) (b c : nat -> A), (forall l : nat, l <= k -> b l = c l) -> (P k b) -> (P k c). + +Definition const_seq {A : Type} (a : A) := (fun l : nat => a). +Definition const_seq_from {A : Type} (a : A) (k : nat) (seq : nat -> A) := + fun l : nat => if (lt_dec l k) then (seq l) else a. + + +Context {A : Type} {P : nat -> (nat -> A) -> Prop} (HypP : dep_only_on_start P). +Context (a0 : A) (H0 : P 0 (const_seq a0)) + (Hstep : forall (k : nat) (prev : nat -> A), + P k prev -> {a : A | P (S k) (const_seq_from a (S k) prev)}). + + +Fixpoint ind_seq_of_seq_and_prop (k : nat) : {seq : nat -> A | P k seq} := + match k with + | O => exist _ (const_seq a0) H0 + | S k => exist _ (const_seq_from + (proj1_sig (Hstep _ _ (proj2_sig (ind_seq_of_seq_and_prop k)))) + (S k) (proj1_sig (ind_seq_of_seq_and_prop k))) + (proj2_sig (Hstep _ _ (proj2_sig (ind_seq_of_seq_and_prop k)))) + end. + + +Definition ind_seq_with_prop : {seq : nat -> A | forall k : nat, P k seq}. +Proof. + set ind_seq_of_seq_and_prop as seq_of_seq. + exists (fun k => proj1_sig (seq_of_seq k) k). (* take the diagonal *) + intro k. + apply (HypP _ (proj1_sig (seq_of_seq k))). + - (* k-th sequence matches diagonal sequence for first k terms *) + intros l Hl. + induction k. + + assert (l = 0) as l_eq_0 by lia. (* l <= 0 implies l = 0 *) + rewrite l_eq_0; reflexivity. + + simpl; unfold const_seq_from. + destruct (lt_dec l (S k)) as [l_lt_Sk | not_l_lt_Sk]. + * apply IHk; lia. + * assert (l = S k) as l_eq_Sk by lia. + rewrite l_eq_Sk. + simpl; unfold const_seq_from. + destruct (lt_dec (S k) (S k)) as [Sk_lt_Sk | not_Sk_lt_Sk]. + -- assert (con : False) by lia; contradiction. + -- reflexivity. + - (* k-th sequence satisfies k-th property *) + exact (proj2_sig (seq_of_seq k)). +Defined. + +End Construction. + + + +Section StrongInduction. + +Lemma quant_over_start_dep_only_on_start {A : Type} {P : nat -> (nat -> A) -> Prop} : + dep_only_on_start P -> dep_only_on_start (fun (k : nat) (seq : nat -> A) => forall l : nat, l <= k -> P l seq). +Proof. + intros HypP k b c start_b_eq_c Hb l l_le_k. + apply (HypP _ b). + - intros i i_le_l. + apply start_b_eq_c; lia. + - apply Hb; assumption. +Qed. + +Lemma reformulate_base_prop_strong {A : Type} (P : nat -> (nat -> A) -> Prop) (seq : nat -> A) : + (P 0 seq) -> (forall l : nat, l <= 0 -> P l seq). +Proof. + intros H0 l l_le_0. + assert (l = 0) as l_eq_0 by lia. + rewrite l_eq_0; exact H0. +Qed. + +Lemma reformulate_final_prop_strong {A : Type} (P : nat -> (nat -> A) -> Prop) (seq : nat -> A) : + (forall k : nat, forall l : nat, l <= k -> P l seq) -> (forall k : nat, P k seq). +Proof. + intros H k. + assert (k <= k) as k_le_k by lia. + exact (H k k k_le_k). +Qed. + + +Theorem strong_ind_seq_with_prop {A : Type} {P : nat -> (nat -> A) -> Prop} (HypP : dep_only_on_start P) + (a0 : A) (H0 : P 0 (const_seq a0)) + (Hstep : forall (k : nat) (prev : nat -> A), + (forall l : nat, l <= k -> P l prev) -> {a : A | forall l : nat, l <= (S k) -> P l (const_seq_from a (S k) prev)}) + : {seq : nat -> A | forall k : nat, P k seq}. +Proof. + enough {seq : nat -> A | forall k : nat, forall l : nat, l <= k -> P l seq} as [seq Hseq]. + { exists seq. + apply reformulate_final_prop_strong; exact Hseq. + } + apply (ind_seq_with_prop (quant_over_start_dep_only_on_start HypP) a0). + - apply reformulate_base_prop_strong; exact H0. + - exact Hstep. +Defined. + +End StrongInduction. + + + +Section StrongInductionIndexSequence. + +(* Definition is_index_seq (n : nat -> nat) := forall k : nat, n (k + 1) > n k. *) + +Definition index_seq_prop_family (Q : nat -> Prop) (k : nat) (n : nat -> nat) := + match k with + | O => Q (n 0) + | S k => Q (n (k + 1)) /\ n k < n (k + 1) + end. + +Lemma dep_only_on_start_index_seq_prop_family (Q : nat -> Prop) : + dep_only_on_start (index_seq_prop_family Q). +Proof. + intros k b c b_eq_c_start Hb. + induction k. + - simpl. + assert (0 <= 0) as O_le_O by lia. + rewrite <- (b_eq_c_start 0 O_le_O). + exact Hb. + - simpl. + assert (k + 1 <= S k) as kplus1_le_Sk by lia. + rewrite <- (b_eq_c_start (k + 1) kplus1_le_Sk). + assert (k <= S k) as k_le_Sk by lia. + rewrite <- (b_eq_c_start k k_le_Sk). + exact Hb. +Qed. + + +Lemma reformulate_base_prop_index {Q : nat -> Prop} (n : nat -> nat) (k : nat) : + Q k -> index_seq_prop_family Q 0 (const_seq k). +Proof. + intro Hk. + simpl. unfold const_seq. + exact Hk. +Qed. + + +Lemma reformulate_final_prop_index {Q : nat -> Prop} {n : nat -> nat} : + (forall k : nat, index_seq_prop_family Q k n) -> is_index_seq n /\ forall k : nat, Q (n k). +Proof. + intro H. + split. + - intro k. + exact (proj2 (H (S k))). + - induction k. + + exact (H O). + + assert (S k = k + 1) as Sk_eq_kplus1 by lia; rewrite Sk_eq_kplus1. + exact (proj1 (H (S k))). +Qed. + +Lemma reformulate_step_prop_index {Q : nat -> Prop} : + (forall (k : nat) (n : nat -> nat), + (forall l : nat, l <= k -> Q (n l)) -> (forall l : nat, l < k -> n l < n (l + 1)) -> + {n_kplus1 : nat | Q n_kplus1 /\ n k < n_kplus1}) + -> + (forall (k : nat) (n : nat -> nat), + (forall l : nat, l <= k -> index_seq_prop_family Q l n) -> + {n_kplus1 : nat | forall l : nat, l <= k + 1 -> index_seq_prop_family Q l (const_seq_from n_kplus1 (k + 1) n)}). +Proof. + intros Hstep k n H. + assert (forall l : nat, l <= k -> Q (n l)) as H1. + { intros l le_k. + induction l. + - apply (H 0 le_k). + - assert (S l = l + 1) as Sl_eq_lplus1 by lia; rewrite Sl_eq_lplus1. + apply (proj1 (H (S l) le_k)). + } + assert (forall l : nat, l < k -> n l < n (l + 1)) as H2. + { intros l lt_k. + apply (proj2 (H (S l) lt_k)). + } + destruct (Hstep k n H1 H2) as [n_kplus1 Hn_kplus1]. + exists n_kplus1. + intros l le_kplus1. + induction l. + - simpl; unfold const_seq_from. + destruct (lt_dec 0 (k + 1)) as [O_lt_kplus1 | not_O_lt_kplus1]. + + assert (0 <= k) as O_le_k by lia. + exact (H1 0 O_le_k). + + exact (proj1 Hn_kplus1). + - simpl; unfold const_seq_from. + destruct (lt_dec (l + 1) (k + 1)) as [lplus1_lt_kplus1 | not_lplus1_lt_kplus1]. + + split. + * assert (l + 1 <= k) as lplus1_le_k by lia. + exact (H1 (l + 1) lplus1_le_k). + * destruct (lt_dec l (k + 1)) as [l_lt_kplus1 | not_l_lt_kplus1]. + -- assert (S l <= k) as Sl_le_k by lia. + exact (proj2 (H (S l) Sl_le_k)). + -- assert False by lia; contradiction. + + split. + * exact (proj1 Hn_kplus1). + * destruct (lt_dec l (k + 1)) as [l_lt_kplus1 | not_l_lt_kplus1]. + -- assert (l = k) as l_eq_k by lia. + rewrite l_eq_k. + exact (proj2 Hn_kplus1). + -- assert False by lia; contradiction. +Qed. + + +Theorem strong_ind_index_seq_with_prop {Q : nat -> Prop} + (H0 : {n_0 : nat | Q n_0}) + (Hstep : forall (k : nat) (n : nat -> nat), + (forall l : nat, l <= k -> Q (n l)) -> (forall l : nat, l < k -> n l < n (l + 1)) -> + {n_kplus1 : nat | Q n_kplus1 /\ n k < n_kplus1}) + : {n : nat -> nat | is_index_seq n /\ forall k : nat, Q (n k)}. +Proof. + enough {n : nat -> nat | forall k : nat, index_seq_prop_family Q k n} as [n Hn]. + { exists n. + apply reformulate_final_prop_index. + exact Hn. + } + destruct H0 as [n0 Hn0]. + apply (strong_ind_seq_with_prop (dep_only_on_start_index_seq_prop_family Q) n0). + - apply (reformulate_base_prop_index (const_seq n0)). + exact Hn0. + - intro k. + assert (S k = k + 1) as Sk_eq_kplus1 by lia; rewrite Sk_eq_kplus1. + apply reformulate_step_prop_index. + apply Hstep. +Qed. + + +Definition dep_choice := non_dep_dep_functional_choice choice. + +Lemma help_with_choice {A B C : Type} {D E : A -> B -> Prop} {P : A -> B -> C -> Prop} : + (forall (a : A) (b : B), D a b -> E a b-> exists c : C, P a b c) -> + (exists f : forall (a : A) (b : B), D a b -> E a b -> C, forall (a : A) (b : B) (d : D a b) (e : E a b), P a b (f a b d e)). +Proof. + intro H. + apply (dep_choice _ _ (fun a f => forall (b : B) (d : D a b) (e : E a b), P a b (f b d e))). + intro a. + apply (dep_choice _ _ (fun b f => forall (d : D a b) (e : E a b), P a b (f d e))). + intro b. + apply (choice (fun d f => forall (e : E a b), P a b (f e))). + intro d. + apply (choice (fun e c => P a b c)). + apply (H a b d). +Qed. + +Theorem classic_strong_ind_index_seq_with_prop {Q : nat -> Prop} + (H0 : exists n_0 : nat, Q n_0) + (Hstep : forall (k : nat) (n : nat -> nat), + (forall l : nat, l <= k -> Q (n l)) -> (forall l : nat, l < k -> n l < n (l + 1)) -> + exists n_kplus1 : nat, Q n_kplus1 /\ n k < n_kplus1) + : exists n : nat -> nat, is_index_seq n /\ forall k : nat, Q (n k). +Proof. + destruct H0 as [n0 Hn0]. + assert {n0 | Q n0} as H0sig by (exists n0; exact Hn0). + (* Transform Hstep using choice such that the existence statement is at the start so it can be destructed. *) + pose (help_with_choice Hstep) as Hstep2. + destruct Hstep2 as [f Hf]. + (* Use strong version to prove existence statement. *) + enough {n : nat -> nat | is_index_seq n /\ (forall k : nat, Q (n k))} as [n Hn]. + { exists n; exact Hn. } + apply (strong_ind_index_seq_with_prop H0sig). + (* Use transformed Hstep to prove strong Hstep condition. *) + intros k n H1 H2. + exists (f k n H1 H2). + apply Hf. +Qed. + +End StrongInductionIndexSequence. + + +(** Tactics *) +(** Warning: we don't (yet) know how to specify dummy variables in existence statements, + so the use of the letters 'n' and 'k' in the tactics is hard-coded. *) + +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). + +Require Import Waterproof.Util.Goals. +Require Import Util.MessagesToUser. + +Local Ltac2 inductive_def_index_seq_n () := + lazy_match! goal with + | [ |- exists n : nat -> nat, is_index_seq n /\ forall k : nat, @?p n k ] => (*@?p*) + let q := eval unfold id in (fun l : nat => $p id l) in + match Control.case (fun () => apply (@classic_strong_ind_index_seq_with_prop $q)) with + | Val _ => Control.focus 2 2 (fun () => apply StrongIndIndxSeq.unwrap) + | Err exn => throw (of_string "The index sequence cannot be defined using this technique.") + end + | [ |- _ ] => throw (of_string "The goal is not to define an index sequence.") + end. + +Ltac2 Notation "Define" "the" "index" "sequence" "n" "inductively" := + panic_if_goal_wrapped (); + inductive_def_index_seq_n (). + +Local Ltac2 take_first_k () := + lazy_match! goal with + | [|- StrongIndIndxSeq.Wrapper _] => apply StrongIndIndxSeq.wrap; intros k n + | [|- _] => throw (of_string "No need to introduce first k elements of sequence n.") + end. + +Ltac2 Notation "Take" "k" ":" "ℕ" "and" "assume" "n(0),...,n(k)" "are" "defined" := + take_first_k (). diff --git a/theories/Libs/Analysis/Subsequences.v b/theories/Libs/Analysis/Subsequences.v index 9043d640..eb07f87b 100644 --- a/theories/Libs/Analysis/Subsequences.v +++ b/theories/Libs/Analysis/Subsequences.v @@ -28,6 +28,8 @@ Require Import Libs.Negation. Require Import Notations. Require Import Tactics. +Require Import Waterprove. + Waterproof Enable Automation RealsAndIntegers. (** ## Creating a subsequence of elements satisfying a certain property @@ -76,8 +78,7 @@ Lemma created_seq_is_index_seq : Proof. Take g : (ℕ → ℕ → ℕ). Assume that (∀ (m N : ℕ), (g m N ≥ N)%nat) (i). - Expand the definition of is_index_seq. - That is, write the goal as (for all k : ℕ, (create_seq g k < create_seq g (S k))%nat). + We need to show that (for all k : ℕ, (create_seq g k < create_seq g (S k))%nat). Take k : ℕ. By (i) it holds that (g (S k) (S (create_seq g k)) ≥ S(create_seq g k))%nat. We conclude that (& create_seq g k < g (S k) (S (create_seq g k)) = create_seq g (S k))%nat. @@ -113,9 +114,9 @@ Proof. Take P : (ℕ → ℝ → Prop). Assume that (for all m N : ℕ, there exists k : ℕ , (N ≤ k)%nat ∧ P m (a k)). By existence_next_el_to_fun it holds that - (∃ g : ℕ → ℕ → ℕ, ∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))) (i). - Obtain g according to (i), so for g : ℕ → ℕ → ℕ it holds that - (∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))) (ii). + (∃ g : ℕ → ℕ → ℕ, ∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))). + Obtain such a g. It holds that + (∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))) (i). Choose n := (create_seq g). We show both statements. - We need to show that (is_index_seq n). @@ -123,7 +124,7 @@ Proof. By created_seq_is_index_seq it suffices to show that (for all m N : ℕ, (g m N ≥ N)%nat). Take m, M : nat. - By (ii) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))). + By (i) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))). We conclude that (g m M >= M)%nat. - We need to show that (for all k : ℕ, P k (a (n k))). We need to show that (for all k : ℕ, P k (a ((create_seq g) k))). @@ -131,12 +132,11 @@ Proof. apply subseq_sat_rel. Take m : ℕ. Take M : ℕ. - By (ii) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))) (iii). - Because (iii) both (M ≤ g m M)%nat and (P m (a (g m M))) hold. + By (i) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))) (ii). + Because (ii) both (M ≤ g m M)%nat and (P m (a (g m M))) hold. We conclude that (P m (a (g m M))). Qed. - Definition is_increasing (f : ℕ → ℕ) := ∀ k : ℕ, (f k ≤ f (S k))%nat. Lemma incr_loc_to_glob : @@ -144,9 +144,8 @@ Lemma incr_loc_to_glob : is_increasing g ⇒ (∀ k l : ℕ, (k ≤ l)%nat ⇒ (g k ≤ g l)%nat). Proof. - Take g : (ℕ → ℕ). - Expand the definition of is_increasing. - That is, write the goal as ((for all k : ℕ, (g k ≤ g (S k))%nat) + Take g : (ℕ → ℕ). + We need to show that ((for all k : ℕ, (g k ≤ g (S k))%nat) ⇨ for all k l : ℕ, (k ≤ l)%nat ⇨ (g k ≤ g l)%nat). Assume that (∀ k : ℕ, (g k ≤ g (S k))%nat). Take k : ℕ. @@ -177,12 +176,10 @@ Qed. Lemma index_seq_strictly_incr : ∀ n : ℕ → ℕ, is_index_seq n ⇒ (is_increasing (fun (k : ℕ) ↦ (n k - k)%nat)). Proof. - Take n : (ℕ → ℕ); such that (is_index_seq n) (i). - Expand the definition of is_increasing. - That is, write the goal as (for all k : ℕ, (n k - k ≤ n (S k) - S k)%nat). + Take n : (ℕ → ℕ); such that (is_index_seq n). + We need to show that (for all k : ℕ, (n(k) - k ≤ n(S(k)) - S(k))%nat). Take k : ℕ. - Expand the definition of is_index_seq in (i). - That is, write (i) as (for all k0 : ℕ, (n k0 < n (S k0))%nat). + It holds that (for all k0 : ℕ, (n k0 < n (S k0))%nat). It holds that (n k < n (S k))%nat. We conclude that (n k - k ≤ n (S k) - S k)%nat. Qed. @@ -192,11 +189,10 @@ Qed. Lemma index_seq_grows_0 : ∀ n : ℕ → ℕ, is_index_seq n ⇒ ∀ k : ℕ, (n k ≥ k)%nat. Proof. - Take n : (ℕ → ℕ); such that (is_index_seq n) (i). + Take n : (ℕ → ℕ); such that (is_index_seq n). induction k as [|k IH]. - We conclude that (n 0 >= 0)%nat. - - Expand the definition of is_index_seq in (i). - That is, write (i) as (for all k0 : ℕ, (n k0 < n (S k0))%nat). + - It holds that (for all k0 : ℕ, (n k0 < n (S k0))%nat). It holds that (n k < n (S k))%nat. We conclude that (n (S k) ≥ S k)%nat. Qed. @@ -261,8 +257,8 @@ Lemma seq_of_max_is_increasing : ∀ g : ℕ → ℕ, is_increasing (seq_of_max g). Proof. Take g : (ℕ → ℕ). - Expand the definition of is_increasing. - That is, write the goal as (for all k : ℕ, (seq_of_max g k ≤ seq_of_max g (S k))%nat). + We need to show that + (for all k : ℕ, (seq_of_max g k ≤ seq_of_max g (S k))%nat). Take k : ℕ. We need to show that (seq_of_max g k ≤ Nat.max (g (S k)) (seq_of_max g k))%nat. We conclude that (seq_of_max g k ≤ Nat.max (g (S k)) (seq_of_max g k))%nat. @@ -274,27 +270,21 @@ Lemma elements_le_seq_of_max_pre : (g n ≤ seq_of_max g n)%nat. Proof. Take g : (ℕ → ℕ). - induction n as [|n IH_n]. - (** We first consider the base case $n=0$.*) - Expand the definition of seq_of_max. - That is, write the goal as ((g 0 ≤ g 0)%nat). - (** We need to show that $f( 0 ) \leq f( 0)$.*) - We conclude that (g 0 ≤ g 0)%nat. - (** Next we consider the general case. We need to show that $f(S(n))\leq \mathsf{seqofmax}(f, S(n))$. *) - Expand the definition of seq_of_max. - That is, write the goal as ((g (S n) ≤ Nat.max (g (S n)) - ((fix seq_of_max (f : ℕ ⇨ ℕ) (l : ℕ) {struct l} : ℕ := - match l with - | 0 => f 0 - | S k => Nat.max (f l) (seq_of_max f k) - end) g n))%nat). - (** By the definition of $\mathsf{seqofmax}(f,S(n))$ as the maximum of $f(S(n))$ and another number, this*) - We conclude that (g (S n) ≤ Nat.max (g (S n)) - ((fix seq_of_max (f : ℕ ⇨ ℕ) (l : ℕ) {struct l} : ℕ := - match l with - | 0 => f 0 - | S k => Nat.max (f l) (seq_of_max f k) - end) g n))%nat. + We use induction on n. + - We first show the base case (g(0) ≤ seq_of_max(g, 0))%nat. + We need to show that (g 0 ≤ g 0)%nat. + We conclude that (g 0 ≤ g 0)%nat. + - We now show the induction step. + Assume that (g(n) <= seq_of_max g n)%nat. + We need to show that (g(n + 1) ≤ + (fix seq_of_max (f : ℕ ⇨ ℕ) (l : ℕ) {struct l} : ℕ := + match l with + | 0 => f(0) + | S k => Nat.max(f(l), seq_of_max(f, k)) + end)(g, n + 1))%nat. + It holds that (n + 1 = S n)%nat (i). + It suffices to show that (g(n) ≤ seq_of_max(g, n))%nat. + We conclude that (g(n) ≤ seq_of_max(g, n))%nat. Qed. @@ -328,8 +318,7 @@ Lemma built_seq_is_index_seq : Proof. Take g : (ℕ → ℕ). Assume that (for all k : ℕ, (g k ≥ k)%nat). - Expand the definition of is_index_seq. - That is, write the goal as (for all k : ℕ, (build_seq g k < build_seq g (S k))%nat). + We need to show that (for all k : ℕ, (build_seq g k < build_seq g (S k))%nat). Take k : ℕ. We need to show that (build_seq g k < g (S (seq_of_max g (build_seq g k))))%nat. It holds that (g( S(seq_of_max g (build_seq g k)))≥ S(seq_of_max g (build_seq g k)))%nat. diff --git a/theories/Libs/Analysis/SubsequencesInduction.v b/theories/Libs/Analysis/SubsequencesInduction.v deleted file mode 100644 index 1b5942c1..00000000 --- a/theories/Libs/Analysis/SubsequencesInduction.v +++ /dev/null @@ -1,572 +0,0 @@ -(******************************************************************************) -(* This file is part of Waterproof-lib. *) -(* *) -(* Waterproof-lib is free software: you can redistribute it and/or modify *) -(* it under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 3 of the License, or *) -(* (at your option) any later version. *) -(* *) -(* Waterproof-lib is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU General Public License for more details. *) -(* *) -(* You should have received a copy of the GNU General Public License *) -(* along with Waterproof-lib. If not, see . *) -(* *) -(******************************************************************************) - -Require Import Coq.Reals.Reals. -Require Import ClassicalChoice. -Require Import Lia. - -Require Import Automation. -Require Import Libs.Negation. -Require Import Notations. -Require Import Tactics. - -Waterproof Enable Automation RealsAndIntegers. - -Definition create_seq_from_vec_0 (A : Type) (a : A) := - fun (n : nat) => a. - -Definition create_seq_from_vec (A : Type) - (k : ℕ) (g : ℕ → A) (a : A) (n : ℕ) := - if lt_dec n (k)%nat (* if n < k *) - then (g n) - else a. - -Local Parameter Q : nat -> Prop. -Definition Z := fun (n : nat) => True. - -Definition complete_induction_help (A : Type) (a : A) - (Su : (ℕ → A) → ℕ → A): ℕ → (ℕ → A). -Proof. - intro m. - induction m as [|m prev_func]. - * exact (fun n : ℕ => a). - * exact (fun n : ℕ => if lt_dec n (S m) then (prev_func n) else - Su prev_func m). -Defined. - -Lemma le0_impl_0 : forall k : nat, (k <= 0)%nat -> k = O. -Proof. - intro k. - auto with zarith. -Defined. - -Definition complete_induction (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): (ℕ → A). -Proof. - exact (fun n : ℕ => (complete_induction_help A a Su) n n). -Defined. - -Lemma complete_induction_help_compare_3 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): forall k : nat, - complete_induction_help A a Su (S k)= - (fun n : ℕ => if lt_dec n (S k) then - ((complete_induction_help A a Su k) n) else - Su (complete_induction_help A a Su k) k). -Proof. - intro k. - reflexivity. -Qed. - -Lemma complete_induction_help_compare_4 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A) (prev_func : nat -> A): forall k : nat, - (fun n : ℕ => if lt_dec n (S k) then - (prev_func n) else - Su prev_func k) (S k) = Su prev_func k. -Proof. - intro k. - change ((if lt_dec (S k) (S k) then prev_func (S k) - else Su prev_func k) = Su prev_func k). - destruct (lt_dec (S k) (S k)). - assert (con : False) by auto with zarith. - destruct con. - reflexivity. -Qed. - -Lemma complete_induction_help_compare_5 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A) (prev_func : nat -> A): forall k : nat, - (fun n : ℕ => if lt_dec n (S k) then - (prev_func n) else - Su prev_func k) = - create_seq_from_vec A (S k) prev_func (Su prev_func k). -Proof. - intro k. - unfold create_seq_from_vec. - reflexivity. -Qed. - -Lemma complete_induction_help_compare_6 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): forall k : nat, - complete_induction_help A a Su (S k) = - create_seq_from_vec A (S k) - (complete_induction_help A a Su k) - (Su (complete_induction_help A a Su k) k). -Proof. - intro k. - unfold create_seq_from_vec. - reflexivity. -Qed. - -Definition complete_induction_prop_help (P : nat -> Prop) : - P O -> (forall k : nat, (forall l : nat, (l <= k)%nat -> P(l)) -> forall l : nat, - (l <= S k)%nat -> P(l) ) -> forall k : nat, forall l : nat, (l <= k)%nat -> P l. -intros H1 H2. -induction k as [| k IHk]. -* intro l. - intro l_le_0. - pose (e := le0_impl_0 l l_le_0). - rewrite e. - exact H1. -* apply H2. - apply IHk. -Defined. - -Definition from_small_to_large_ind (P : nat -> Prop) : -(forall k : nat, (forall l : nat, (l <= k)%nat -> P(l)) -> - P (S k) ) -> (forall k : nat, (forall l : nat, (l <= k)%nat -> P(l)) -> forall l : nat, -(l <= S k)%nat -> P(l) ). -Proof. - intro H. - intro k. - intro H1. - intro l. - intro H2. - destruct (lt_eq_lt_dec l (S k)) as [H3 | H4]. - destruct H3 as [H5 | H6]. - assert (H7 : (l <=k)%nat ) by (auto with zarith). - auto. - rewrite H6. - apply H. - assumption. - auto with zarith. -Defined. - -Definition complete_induction_prop (P : nat -> Prop) : - P O -> (forall k : nat, - (forall l : nat, (l <= k)%nat -> P(l)) -> - P(S k) ) -> forall k : nat, P k. -Proof. -intros H1 H2. -intro k. -apply (complete_induction_prop_help P H1 (from_small_to_large_ind P H2) - k k). - trivial. -Defined. -Require Import Arith.Wf_nat. - -Lemma complete_induction_help_compare_1 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): forall k : nat, - (complete_induction_help A a Su (S k) k = - complete_induction_help A a Su (k) k). -Proof. - intro k. - simpl. - destruct (lt_dec k (S k)). - * reflexivity. - * assert (H: False) by auto with zarith. - destruct H. -Defined. - -Lemma complete_induction_help_compare_2 (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): forall k : nat, forall l : nat, (l <= k)%nat -> - (complete_induction_help A a Su (S k) l = - complete_induction_help A a Su (l) l). - Proof. - intro k. - intro l. - induction k as [|k IHk]. - intro l_le_0. - assert (l_eq_0 : (l = 0)%nat) by auto with zarith. - rewrite l_eq_0. - simpl. - reflexivity. - intro l_le_Sk. - destruct (lt_dec l (S k)). - rewrite <- IHk. - simpl. - destruct (lt_dec l (S (S k))). - destruct (lt_dec l (S k)). - reflexivity. - reflexivity. - assert (H : False) by auto with zarith. - destruct H. - auto with zarith. - assert (H1 : l = S k) by auto with zarith. - rewrite H1. - apply complete_induction_help_compare_1. -Qed. - -Lemma complete_induction_help_compare (A : Type) (a : A) -(Su : (ℕ → A) → ℕ → A): forall k : nat, forall l : nat, - (l <= k)%nat -> - (complete_induction_help A a Su k l = - complete_induction A a Su l). -Proof. - unfold complete_induction. - intro k. intro l. intro l_le_k. - destruct (lt_eq_lt_dec l k) as [Z1 | Z2]. - destruct Z1 as [Z3 | Z4]. - set (m := (k - 1)%nat). - assert (H : k = S m) by auto with zarith. - rewrite H. - apply complete_induction_help_compare_2. - auto with zarith. - rewrite Z4. - reflexivity. - assert (H : False) by auto with zarith. - destruct H. -Defined. - -Lemma new_induction_principle : forall A : Type, - forall P : (nat -> A) -> nat -> Prop, - (forall f g : nat -> A, forall k : nat, (forall l : nat, (l <= k)%nat -> f l = g l) -> P f k -> P g k) -> - forall Su : (nat -> A) -> nat -> A, - forall a : A, forall w : P (fun n : nat => a) 0%nat, - (forall k : nat, - (forall l : nat, (l <= k)%nat -> P ((complete_induction_help A a Su) l) l) - -> P ((complete_induction_help A a Su) (S k)) (S k)) - -> forall k : nat, P (complete_induction A a Su) k. -Proof. - intros A P H_ext Su a w H_ind. - intro k. - apply (H_ext (complete_induction_help A a Su (k)) - (complete_induction A a Su)). - unfold complete_induction. - apply complete_induction_help_compare. - induction k as [|k IHk] using complete_induction_prop. - simpl. - exact w. - apply H_ind. - apply IHk. -Qed. - -(* Construct a subsequence with a certain property ... *) - -Definition myP (myQ: nat->nat->Prop) (n : nat -> nat) k : Prop := - match k with - | O => myQ (n O) O - | S m => ((n k) > n (Nat.pred k))%nat /\ (myQ (n k) k) - end. - -Lemma help_test_1 : forall (myQ: nat->nat->Prop), -forall (f g : ℕ ⇨ ℕ)(l : ℕ), -(for all k : ℕ, - (k ≤ l)%nat ⇨ f (k) = g (k)) ⇨ myP myQ f l ⇨ myP myQ g l. -Proof. - intros myQ f g l. - intro H. - unfold myP. - intro myPf. - destruct l. - rewrite <- H. - apply myPf. - trivial. - rewrite <- H. - rewrite <- (H (Nat.pred (S l))). - apply myPf. - auto with zarith. - auto with zarith. -Qed. - -(** The next definition captures what it means to be an index sequence.*) -Definition is_index_seq (n : ℕ → ℕ) := - ∀ k : ℕ, (n k < n (S k))%nat. -(** Given the function that produces 'good' elements, we can use it to construct a subsequence by induction.*) -Fixpoint create_seq - (f : ℕ → ℕ → ℕ) (l : ℕ) := - match l with - | O => f O O - | S k => f l (S (create_seq f k)) - end. - - - -(** If the function that produces 'good' elements is such that the produced elements are far enough in the sequence, it is certain that the produced sequence is an index sequence.*) -Lemma created_seq_is_index_seq : - ∀ (g : ℕ → ℕ → ℕ), - (∀ (m N : ℕ), (g m N ≥ N)%nat ) ⇒ - is_index_seq (create_seq g). -Proof. - Take g : (ℕ → ℕ → ℕ). - Assume that (∀ (m N : ℕ), (g m N ≥ N)%nat) (i). - Expand the definition of is_index_seq. - That is, write the goal as (for all k : ℕ, (create_seq g k < create_seq g (S k))%nat). - Take k : ℕ. - By (i) it holds that (g (S k) (S (create_seq g k)) ≥ S(create_seq g k))%nat. - We conclude that (& create_seq g k < g (S k) (S (create_seq g k)) = create_seq g (S k))%nat. -Qed. - - -(** -The next lemma records that indeed the elements in the subsequence satisfy the desired property.*) -Lemma subseq_sat_rel : - ∀ (a : ℕ → ℝ) (g : ℕ → ℕ → ℕ) (P : ℕ → ℝ → Prop), - (∀ m N : ℕ, P m (a (g m N)) ) ⇒ - ∀ k : ℕ, P k (a (create_seq g k)). -Proof. - Take a : (ℕ → ℝ). - Take g : (ℕ → ℕ → ℕ). - Take P : (ℕ → ℝ → Prop). - Assume that (∀ m N : ℕ, P m (a (g m N))) (i). - induction k. (*TODO: if we use 'We use induction on k.'-tacftic, then we cannot directly match on k + 1 *) - - We need to show that (P 0%nat (a (g 0%nat 0%nat))). - By (i) we conclude that (P 0%nat (a (g 0%nat 0%nat))). - - We need to show that (P (S k) (a (g (S k) (S (create_seq g k))))). - By (i) we conclude that (P (S k) (a (g (S k) (S (create_seq g k))))). -Qed. - -Lemma existence_next_el_to_fun : - ∀ (a : ℕ → ℝ) (P : ℕ → ℝ → Prop), - (∀ (m : ℕ) (N : ℕ), ∃ k : ℕ, (N ≤ k)%nat ∧ (P m (a k))) ⇒ - ∃ f : ℕ → ℕ → ℕ, ∀ (m : ℕ) (N : ℕ), (N ≤ f m N)%nat ∧ P m (a (f m N)). -Proof. - Take a : (ℕ → ℝ). - Take P : (ℕ → ℝ → Prop). - Assume that (for all m N : ℕ, there exists k : ℕ , (N ≤ k)%nat ∧ P m (a k)) (i). - We claim that (∀ (m : ℕ), ∃ g : ℕ → ℕ, ∀ N : ℕ, (N ≤ g N)%nat ∧ (P m (a (g N)))) (ii). - { - Take m : ℕ. - apply choice with (R := fun (k : ℕ) (l : ℕ) ↦ ((k ≤ l)%nat ∧ P m (a l))). - By (i) we conclude that (for all x : ℕ, there exists y : ℕ, (x ≤ y)%nat ∧ P m (a y)). - } - apply choice with (R := fun (m : ℕ) (h : ℕ → ℕ) ↦ ( ∀ N : ℕ, (N ≤ h N)%nat ∧ P m (a (h N)) ) ). - By (ii) we conclude that (for all x : ℕ, there exists y : ℕ ⇨ ℕ , for all N : ℕ, (N ≤ y N)%nat ∧ P x (a (y N))). -Qed. - -Lemma exists_good_subseq : - ∀ (a : ℕ → ℝ) (P : ℕ → ℝ → Prop), - (∀ (m : ℕ) (N : ℕ), ∃ k : ℕ, (N ≤ k)%nat ∧ (P m (a k))) ⇒ - ∃ n : ℕ → ℕ, is_index_seq n ∧ ∀ k : ℕ, P k (a (n k)). -Proof. - Take a : (ℕ → ℝ). - Take P : (ℕ → ℝ → Prop). - Assume that (for all m N : ℕ, there exists k : ℕ , (N ≤ k)%nat ∧ P m (a k)). - By existence_next_el_to_fun it holds that - (∃ g : ℕ → ℕ → ℕ, ∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))) (i). - Obtain g according to (i), so for g : ℕ → ℕ → ℕ it holds that - (∀ (m : ℕ) (N : ℕ), (N ≤ g m N)%nat ∧ P m (a (g m N))) (ii). - Choose n := (create_seq g). - We show both statements. - - We need to show that (is_index_seq n). - We need to show that (is_index_seq (create_seq g)). - By created_seq_is_index_seq it suffices to show that - (for all m N : ℕ, (g m N ≥ N)%nat). - Take m, M : nat. - By (ii) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))). - We conclude that (M <= g m M)%nat. - - We need to show that (for all k : ℕ, P k (a (n k))). - We need to show that (for all k : ℕ, P k (a ((create_seq g) k))). - Fail By subseq_sat_rel it suffices to show that (for all m N : ℕ, P m (a (g m N))). (*TODO: fix*) - apply subseq_sat_rel. - Take m : ℕ. - Take M : ℕ. - By (ii) it holds that ((M ≤ g m M)%nat ∧ P m (a (g m M))) (iii). - Because (iii) both (M ≤ g m M)%nat and (P m (a (g m M))) hold. - We conclude that (P m (a (g m M))). -Qed. - - -Definition is_increasing (f : ℕ → ℕ) := - ∀ k : ℕ, (f k ≤ f (S k))%nat. -Lemma incr_loc_to_glob : - ∀ g : ℕ → ℕ, - is_increasing g - ⇒ (∀ k l : ℕ, (k ≤ l)%nat ⇒ (g k ≤ g l)%nat). -Proof. - Take g : (ℕ → ℕ). - Expand the definition of is_increasing. - That is, write the goal as ((for all k : ℕ, (g k ≤ g (S k))%nat) - ⇨ for all k l : ℕ, (k ≤ l)%nat ⇨ (g k ≤ g l)%nat). - Assume that (∀ k : ℕ, (g k ≤ g (S k))%nat). - Take k : ℕ. - induction l as [|l IH_l]. - - (** We first need to show that if $k \leq 0$ then $(f (k) \leq f(0))$.*) - Assume that (k ≤ 0)%nat. - It holds that (k = 0)%nat. - We conclude that (& g k = g 0 <= g 0)%nat. - - (** Next, we need to show that if $k \leq S (l)$ then $f(k) \leq f(S (l))$.*) - Assume that (k ≤ S l)%nat. - destruct (lt_eq_lt_dec k (S l)) as [temp | k_gt_Sl]. - destruct temp as [k_lt_Sl | k_eq_Sl]. - (** We first consider the case that $k < S(l)$.*) - It holds that (k ≤ l)%nat. - By IH_l it holds that (g k ≤ g l)%nat. - It holds that (g l ≤ g (S l))%nat. - We conclude that (g k <= g (S l))%nat. - (** We now consider the case $k = S(l)$. We need to show that $f(k) \leq f(S(l))$. *) - We conclude that (& g k = g (S l) <= g (S l))%nat. - (** Finally we consider the case $k > S(l)$. However, this case is in contradiction with $k \leq S(l)$. *) - It holds that (¬(S l < k)%nat). - Contradiction. -Qed. - - -Lemma index_seq_strictly_incr : - ∀ n : ℕ → ℕ, is_index_seq n ⇒ (is_increasing (fun (k : ℕ) ↦ (n k - k)%nat)). -Proof. - Take n : (ℕ → ℕ); such that (is_index_seq n) (i). - Expand the definition of is_increasing. - That is, write the goal as (for all k : ℕ, (n k - k ≤ n (S k) - S k)%nat). - Take k : ℕ. - Expand the definition of is_index_seq in (i). - That is, write (i) as (for all k0 : ℕ, (n k0 < n (S k0))%nat). - It holds that (n k < n (S k))%nat. - We conclude that (n k - k ≤ n (S k) - S k)%nat. -Qed. - -Lemma index_seq_grows_0 : - ∀ n : ℕ → ℕ, is_index_seq n ⇒ ∀ k : ℕ, (n k ≥ k)%nat. -Proof. - Take n : (ℕ → ℕ); such that (is_index_seq n) (i). - induction k as [|k IH]. - - We conclude that (n 0 >= 0)%nat. - - Expand the definition of is_index_seq in (i). - That is, write (i) as (for all k0 : ℕ, (n k0 < n (S k0))%nat). - It holds that (n k < n (S k))%nat. - We conclude that (n (S k) ≥ S k)%nat. -Qed. - -Lemma index_seq_grows : - ∀ n : ℕ → ℕ, is_index_seq n ⇒ (∀ k l : ℕ, (k ≤ l)%nat ⇒ (n k - k ≤ n l - l)%nat). -Proof. - Take n : (ℕ → ℕ); such that (is_index_seq n). - Define g := (fun (k : ℕ) ↦ (n k - k)%nat). - By index_seq_strictly_incr it holds that (is_increasing g). - By incr_loc_to_glob it holds that (∀ k l : ℕ, (k ≤ l)%nat ⇒ (g k ≤ g l)%nat). - Take k : ℕ and l : ℕ; such that(k ≤ l)%nat. - We need to show that (g k <= g l)%nat. - We conclude that (g k <= g l)%nat. -Qed. - - - -(** ## Constructing a subsequence with elements satisfying a particular property - -Given a property $P : \mathbb{N} → \mathbb{R} → \mathrm{Prop}$, and given that it holds for infinitely many elements in the sequence, we want to find a subsequence with all elements satisfying the property. *) -(** ### Finding the smallest element satisfying the property - -This seems to require some decidability on the property*) -Fixpoint first_satisfying_element_helper - (rel : ℕ → ℕ → bool) - (k : ℕ) - (N : ℕ) := - match k with - | O => N - | S l => if (rel (N-k)%nat (N-k)%nat) - then k - else (first_satisfying_element_helper rel l N) - end. -Definition first_satisfying_element - (rel : ℕ → ℕ → bool) - (l : ℕ) - (N : ℕ) - := first_satisfying_element_helper rel (N-l) N. -(** ### From infinitely many elements to a function producing those elements*) -Lemma inf_el_to_fun : - ∀ (a : ℕ → ℝ) (P : ℕ → ℝ → Prop), - (∀ N : ℕ, ∃ k : ℕ, (N ≤ k)%nat ∧ (P N (a k))) ⇒ - ∃ f : ℕ → ℕ, ∀ l : ℕ, (l ≤ f l)%nat ∧ P l (a (f l)). -Proof. - Take a : (ℕ → ℝ). - Take P : (ℕ → ℝ → Prop). - apply choice with (R := fun (k : ℕ) (l : ℕ) ↦ ((k ≤ l)%nat ∧ P k (a l))). -Qed. - - -Fixpoint seq_of_max (f : ℕ → ℕ) (l : ℕ) := - match l with - | O => f O - | S k => Nat.max (f l) (seq_of_max f k) - end. - - -(** ### The sequence of maxima is increasing*) -Lemma seq_of_max_is_increasing : - ∀ g : ℕ → ℕ, is_increasing (seq_of_max g). -Proof. - Take g : (ℕ → ℕ). - Expand the definition of is_increasing. - That is, write the goal as (for all k : ℕ, (seq_of_max g k ≤ seq_of_max g (S k))%nat). - Take k : ℕ. - We need to show that (seq_of_max g k ≤ Nat.max (g (S k)) (seq_of_max g k))%nat. - We conclude that (seq_of_max g k ≤ Nat.max (g (S k)) (seq_of_max g k))%nat. -Qed. - - -Lemma elements_le_seq_of_max_pre : - ∀ (g : ℕ → ℕ) (n : ℕ), - (g n ≤ seq_of_max g n)%nat. -Proof. - Take g : (ℕ → ℕ). - induction n as [|n IH_n]. - (** We first consider the base case $n=0$.*) - Expand the definition of seq_of_max. - That is, write the goal as ((g 0 ≤ g 0)%nat). - (** We need to show that $f( 0 ) \leq f( 0)$.*) - We conclude that (g 0 ≤ g 0)%nat. - (** Next we consider the general case. We need to show that $f(S(n))\leq \mathsf{seqofmax}(f, S(n))$. *) - Expand the definition of seq_of_max. - That is, write the goal as ((g (S n) ≤ Nat.max (g (S n)) - ((fix seq_of_max (f : ℕ ⇨ ℕ) (l : ℕ) {struct l} : ℕ := - match l with - | 0 => f 0 - | S k => Nat.max (f l) (seq_of_max f k) - end) g n))%nat). - (** By the definition of $\mathsf{seqofmax}(f,S(n))$ as the maximum of $f(S(n))$ and another number, this*) - We conclude that (g (S n) ≤ Nat.max (g (S n)) - ((fix seq_of_max (f : ℕ ⇨ ℕ) (l : ℕ) {struct l} : ℕ := - match l with - | 0 => f 0 - | S k => Nat.max (f l) (seq_of_max f k) - end) g n))%nat. -Qed. - -Lemma elements_le_seq_of_max : - ∀ (g : ℕ → ℕ) (n : ℕ) (k : ℕ), - (k ≤ n)%nat ⇒ (g k ≤ seq_of_max g n)%nat. -Proof. - Take g : (ℕ → ℕ). - Take n : ℕ and k : ℕ; such that (k ≤ n)%nat. - By elements_le_seq_of_max_pre it holds that (g k ≤ seq_of_max g k)%nat. - We claim that (seq_of_max g k ≤ seq_of_max g n)%nat. - { By incr_loc_to_glob it suffices to show that (is_increasing (seq_of_max g)). - By seq_of_max_is_increasing we conclude that (is_increasing (seq_of_max g)). - } - We conclude that (& g k <= seq_of_max g k <= seq_of_max g n)%nat. -Qed. - - -(** ### From a function producing the correct elements to an index sequence*) -Fixpoint build_seq - (f : ℕ → ℕ) - (k : ℕ) := - match k with - | O => f O - | S m => f (S (seq_of_max f (build_seq f m))) - end. -Lemma built_seq_is_index_seq : - ∀ g : ℕ → ℕ, - (∀ k : ℕ, (g k ≥ k)%nat) ⇒ - is_index_seq (build_seq g). -Proof. - Take g : (ℕ → ℕ). - Assume that (for all k : ℕ, (g k ≥ k)%nat). - Expand the definition of is_index_seq. - That is, write the goal as (for all k : ℕ, (build_seq g k < build_seq g (S k))%nat). - Take k : ℕ. - We need to show that (build_seq g k < g (S (seq_of_max g (build_seq g k))))%nat. - It holds that (g( S(seq_of_max g (build_seq g k)))≥ S(seq_of_max g (build_seq g k)))%nat. - It holds that (g( S(seq_of_max g (build_seq g k)))> seq_of_max g (build_seq g k))%nat. - By elements_le_seq_of_max_pre it holds that (seq_of_max g (build_seq g k) ≥ g(build_seq g k))%nat. - It holds that (g(build_seq g k) ≥ build_seq g k)%nat. - We conclude that (& build_seq g k <= g(build_seq g k) - <= seq_of_max g (build_seq g k) - < g( S(seq_of_max g (build_seq g k))))%nat. -Qed. -(** ### Subsequence satisfies relation*) diff --git a/theories/Libs/Analysis/SubsequencesMetric.v b/theories/Libs/Analysis/SubsequencesMetric.v index f45a7d20..18ae20f7 100644 --- a/theories/Libs/Analysis/SubsequencesMetric.v +++ b/theories/Libs/Analysis/SubsequencesMetric.v @@ -35,12 +35,12 @@ Notation "n 'is' 'an' '_index' 'sequence_'" := (is_index_sequence n) (at level 6 Notation "n 'is' 'an' 'index' 'sequence'" := (is_index_sequence n) (at level 68, only parsing) : metric_scope. -Local Ltac2 unfold_is_index_sequence () := unfold is_index_sequence. +Local Ltac2 unfold_is_index_sequence (statement : constr) := eval unfold is_index_sequence in $statement. -Local Ltac2 unfold_is_index_sequence_in (h : ident) := unfold is_index_sequence in $h. - -Ltac2 Notation "Expand" "the" "definition" "of" "index" "sequence" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_index_sequence unfold_is_index_sequence_in cl. +Ltac2 Notation "Expand" "the" "definition" "of" "index" "sequence" "in" statement(constr) := + unfold_in_statement unfold_is_index_sequence (Some "index sequence") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "index" "sequence" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_index_sequence (Some "index sequence") statement. (** The next definition captures what it means to be an index sequence.*) @@ -117,13 +117,12 @@ Lemma incr_loc_to_glob : Proof. (* There exists already a constant called [f].*) Take g : (ℕ → ℕ). - Expand the definition of is_increasing. (*TODO: the layout of is_increasing is confusing*) - That is, write the goal as + We need to show that ((for all k : ℕ, (g k ≤ g (k + 1))%nat) ⇨ for all k l : ℕ, (k ≤ l ⇨ g k ≤ g l)%nat ). Assume that (∀ k : ℕ, (g k) ≤ (g (k + 1)))%nat. Take k : ℕ. We use induction on l. - - We first show the base case, namely ((k ≤ 0)%nat ⇨ (g k ≤ g 0)%nat). + - We first show the base case ((k ≤ 0)%nat ⇨ (g k ≤ g 0)%nat). Assume that (k ≤ 0)%nat. It holds that (k = 0)%nat. It suffices to show that (g k = g 0)%nat. @@ -154,8 +153,7 @@ Proof. Take k1, k2 : ℕ; such that (k1 ≥ k2)%nat. We need to show that (n k1 ≥ n k2)%nat. By incr_loc_to_glob it suffices to show that (is_increasing n). - Expand the definition of is_increasing. - That is, write the goal as (for all k : ℕ, (n k ≤ n (k + 1))%nat). + We need to show that (for all k : ℕ, (n k ≤ n (k + 1))%nat). Take k : ℕ. We conclude that (n k ≤ n (k+1))%nat. Qed. @@ -183,10 +181,8 @@ Assume that (is_index_sequence n). It suffices to show that (∀ ε : ℝ, ε > 0 ⇒ ∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat ⇒ dist _ (a (n k)) p < ε). Take ε : ℝ; such that (ε > 0). -It holds that (∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat → dist _ (a k) p < ε) (i). -Obtain K according to (i), so for K : nat it holds that - (∀ k : ℕ, (k ≥ K)%nat → dist _ (a k) p < ε). -Choose N3 := K. +It holds that (∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat → dist _ (a k) p < ε). +Obtain such a K. Choose N3 := K. Take k : ℕ; such that (k ≥ N3)%nat. By index_sequence_property2 it holds that (n k ≥ n K)%nat. By index_sequence_property it holds that (n K ≥ K)%nat. @@ -205,19 +201,17 @@ Take p : X. Assume that (x ⟶ p). We need to show that (y ⟶ p). -It holds that (∃ m : ℕ → ℕ, is_index_sequence m ∧ ∀ k : ℕ, y k = (x ◦ m) k) (i). -Obtain m according to (i), so for m : nat -> nat it holds that - (is_index_sequence m ∧ ∀ k : ℕ, y k = (x ◦ m) k) (ii). -Because (ii) both (is_index_sequence m) and +It holds that (∃ m : ℕ → ℕ, is_index_sequence m ∧ ∀ k : ℕ, y k = (x ◦ m) k). +Obtain such an m. It holds that + (is_index_sequence m ∧ ∀ k : ℕ, y k = (x ◦ m) k) (i). +Because (i) both (is_index_sequence m) and (for all k : nat, y k = x (m k)) hold. It suffices to show that (∀ ε : ℝ, ε > 0 ⇒ ∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat ⇒ dist _ (y k) p < ε). Take ε : ℝ; such that (ε > 0). -It holds that (∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat → dist _ (x k) p < ε) (iii). -Obtain K according to (iii), so for K : nat it holds that - (∀ k : ℕ, (k ≥ K)%nat → dist _ (x k) p < ε). -Choose N3 := K. +It holds that (∃ N3 : ℕ, ∀ k : ℕ, (k ≥ N3)%nat → dist _ (x k) p < ε). +Obtain such a K. Choose N3 := K. Take k : ℕ; such that (k ≥ N3)%nat. By index_sequence_property2 it holds that (m k ≥ m K)%nat. By index_sequence_property it holds that (m K ≥ K)%nat. @@ -235,22 +229,24 @@ End my_section. Notation "b 'is' 'a' '_subsequence_' 'of' a" := (is_subsequence _ b a) (at level 68) : metric_scope. Notation "b 'is' 'a' 'subsequence' 'of' a" := (is_subsequence _ b a) (at level 68, only parsing) : metric_scope. -Local Ltac2 unfold_is_subsequence () := unfold is_subsequence. -Local Ltac2 unfold_is_subsequence_in (h : ident) := unfold is_subsequence in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "subsequence" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_subsequence unfold_is_subsequence_in cl. +Local Ltac2 unfold_is_subsequence (statement : constr) := eval unfold is_subsequence in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "subsequence" "in" statement(constr) := + unfold_in_statement unfold_is_subsequence (Some "subsequence") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "subsequence" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_subsequence (Some "subsequence") statement. Notation "p 'is' 'an' '_accumulation' 'point_' 'of' a" := (is_accumulation_point _ p a) (at level 68) : metric_scope. Notation "p 'is' 'an' 'accumulation' 'point' 'of' a" := (is_accumulation_point _ p a) (at level 68, only parsing) : metric_scope. -Local Ltac2 unfold_is_accumulation_point () := unfold is_accumulation_point. -Local Ltac2 unfold_is_accumulation_point_in (h : ident) := unfold is_accumulation_point in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "accumulation point" cl(opt(seq("in", "(", ident, ")"))) := - expand_def_framework unfold_is_accumulation_point unfold_is_accumulation_point_in cl. +Local Ltac2 unfold_is_accumulation_point (statement : constr) := eval unfold is_accumulation_point in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "accumulation point" "in" statement(constr) := + unfold_in_statement unfold_is_accumulation_point (Some "accumulation point") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "accumulation point" "in" statement(constr) := + unfold_in_statement_no_error unfold_is_accumulation_point (Some "accumulation point") statement. #[export] Hint Resolve index_sequence_property : subsequences. #[export] Hint Extern 1 => (unfold ge) : subsequences. -#[export] Hint Resolve double_is_even : subsequences. +#[export] Hint Resolve double_is_even : wp_integers. #[export] Hint Resolve index_sequence_property2 : subsequences. Close Scope metric_scope. diff --git a/theories/Libs/Analysis/SupAndInf.v b/theories/Libs/Analysis/SupAndInf.v index e4d54c8a..ebe3a17e 100644 --- a/theories/Libs/Analysis/SupAndInf.v +++ b/theories/Libs/Analysis/SupAndInf.v @@ -34,35 +34,38 @@ Notation is_sup := is_lub. (* Implement notations for these concepts. *) Notation "M 'is' 'the' '_supremum_' 'of' A" := (is_lub A M) (at level 69). Notation "M 'is' 'the' 'supremum' 'of' A" := (is_lub A M) (at level 69, only parsing). -Local Ltac2 unfold_is_lub () := unfold is_lub. -Local Ltac2 unfold_is_lub_in (h : ident) := unfold is_lub in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "supremum" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_lub unfold_is_lub_in cl. +Local Ltac2 unfold_is_lub (statement : constr) := eval unfold is_lub in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "supremum" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_lub (Some "supremum") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "supremum" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_lub (Some "supremum") statement. Notation "A 'is' '_bounded' 'from' 'above_'" := (bound A) (at level 69). Notation "A 'is' 'bounded' 'from' 'above'" := (bound A) (at level 69, only parsing). -Local Ltac2 unfold_bound () := unfold bound. -Local Ltac2 unfold_bound_in (h : ident) := unfold bound in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "from" "above" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_bound unfold_bound_in cl. - -Notation "M 'is' 'an' '_upper' 'bound_' 'of' A" := (is_upper_bound A M) (at level 69). -Notation "M 'is' 'an' 'upper' 'bound' 'of' A" := (is_upper_bound A M) (at level 69, only parsing). -Local Ltac2 unfold_is_upper_bound () := unfold is_upper_bound. -Local Ltac2 unfold_is_upper_bound_in (h : ident) := unfold is_upper_bound in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "upper" "bound" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_upper_bound unfold_is_upper_bound_in cl. +Local Ltac2 unfold_bound (statement : constr) := eval unfold bound in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "from" "above" "in" statement(constr) := + Unfold.unfold_in_statement unfold_bound (Some "bounded from above") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "bounded" "from" "above" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_bound (Some "bounded from above") statement. + +Notation "M 'is' 'an' '_upper' 'bound_' 'for' A" := (is_upper_bound A M) (at level 69). +Notation "M 'is' 'an' 'upper' 'bound' 'for' A" := (is_upper_bound A M) (at level 69, only parsing). +Local Ltac2 unfold_is_upper_bound (statement : constr) := eval unfold is_upper_bound in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "upper" "bound" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_upper_bound (Some "upper bound") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "upper" "bound" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_upper_bound (Some "upper bound") statement. (** Maximum *) -Definition is_max (A : ℝ -> Prop) (x : ℝ) := (A x) ∧ (x is an upper bound of A). +Definition is_max (A : ℝ -> Prop) (x : ℝ) := (A x) ∧ (x is an upper bound for A). Notation "M 'is' 'the' '_maximum_' 'of' A" := (is_max A M) (at level 69). Notation "M 'is' 'the' 'maximum' 'of' A" := (is_max A M) (at level 69, only parsing). -Local Ltac2 unfold_is_max () := unfold is_max. -Local Ltac2 unfold_is_max_in (h : ident) := unfold is_max in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "maximum" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_max unfold_is_max_in cl. - +Local Ltac2 unfold_is_max (statement : constr) := eval unfold is_max in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "maximum" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_max (Some "maximum") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "maximum" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_max (Some "maximum") statement. (** ## The completeness axiom @@ -76,8 +79,8 @@ Proof. Assume that (A is bounded from above) (i). We claim that (there exists x : ℝ, A x). { Choose (a). We conclude that (A a). } - By completeness it holds that ({M | M is the supremum of A}) (ii). - Obtain M according to (ii), so for M : ℝ it holds that (M is the supremum of A). + By completeness it holds that ({M | M is the supremum of A}). + Obtain such an M. Choose (M). We conclude that (M is the supremum of A). Qed. @@ -107,25 +110,29 @@ Definition is_inf := (* Implement notations for these concepts. *) Notation "m 'is' 'the' '_infimum_' 'of' A" := (is_inf A m) (at level 69). Notation "m 'is' 'the' 'infimum' 'of' A" := (is_inf A m) (at level 69, only parsing). -Local Ltac2 unfold_is_inf () := unfold is_inf. -Local Ltac2 unfold_is_inf_in (h : ident) := unfold is_inf in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "infimum" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_inf unfold_is_inf_in cl. +Local Ltac2 unfold_is_inf (statement : constr) := eval unfold is_inf in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "infimum" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_inf (Some "infimum") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "infimum" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_inf (Some "infimum") statement. Notation "A 'is' '_bounded' 'from' 'below_'" := (is_bounded_below A) (at level 69). Notation "A 'is' 'bounded' 'from' 'below'" := (is_bounded_below A) (at level 69, only parsing). -Local Ltac2 unfold_is_bounded_below () := unfold is_bounded_below. -Local Ltac2 unfold_is_bounded_below_in (h : ident) := unfold is_bounded_below in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "from" "below" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_bounded_below unfold_is_bounded_below_in cl. - -Notation "M 'is' 'a' '_lower' 'bound_' 'of' A" := (is_lower_bound A M) (at level 69). -Notation "M 'is' 'a' 'lower' 'bound' 'of' A" := (is_lower_bound A M) (at level 69, only parsing). -Local Ltac2 unfold_is_lower_bound () := unfold is_lower_bound. -Local Ltac2 unfold_is_lower_bound_in (h : ident) := unfold is_lower_bound in $h. -Ltac2 Notation "Expand" "the" "definition" "of" "lower" "bound" cl(opt(seq("in", "(", ident, ")"))) := - Unfold.expand_def_framework unfold_is_lower_bound unfold_is_lower_bound_in cl. - +Local Ltac2 unfold_is_bounded_below (statement : constr) := + eval unfold is_bounded_below in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "bounded" "from" "below" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_bounded_below (Some "bounded from below") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "bounded" "from" "below" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_bounded_below (Some "bounded from below") statement. + + +Notation "M 'is' 'a' '_lower' 'bound_' 'for' A" := (is_lower_bound A M) (at level 69). +Notation "M 'is' 'a' 'lower' 'bound' 'for' A" := (is_lower_bound A M) (at level 69, only parsing). +Local Ltac2 unfold_is_lower_bound (statement : constr) := eval unfold is_lower_bound in $statement. +Ltac2 Notation "Expand" "the" "definition" "of" "lower" "bound" "in" statement(constr) := + Unfold.unfold_in_statement unfold_is_lower_bound (Some "lower bound") statement. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" "lower" "bound" "in" statement(constr) := + Unfold.unfold_in_statement_no_error unfold_is_lower_bound (Some "lower bound") statement. (** ## Reflection of a subset of ℝ in the origin @@ -141,14 +148,12 @@ Definition set_opp (A : ℝ -> Prop) := (fun x ↦ (A (-x))). (** Hint Resolve neg_opp_is_original_elem : additional.*) Lemma upp_bd_set_to_low_bd_set_opp : ∀ (A : ℝ → Prop) (M : ℝ), - M is an upper bound of A ⇒ + M is an upper bound for A ⇒ is_lower_bound (set_opp A) (-M). Proof. Take A : (ℝ → Prop) and M : ℝ. - Assume that (M is an upper bound of A) (i). + Assume that (M is an upper bound for A) (i). We need to show that (∀ a : ℝ, (set_opp A a) ⇒ -M ≤ a). - Expand the definition of lower bound. - That is, write the goal as (for all a : ℝ, set_opp A a ⇨ -M ≤ a). Take b : ℝ. Assume that (set_opp A b). Define a := (-b). It holds that (A a). @@ -159,12 +164,11 @@ Qed. Lemma low_bd_set_to_upp_bd_set_opp : ∀ (A : ℝ → Prop) (m : ℝ), is_lower_bound A m ⇒ - -m is an upper bound of (set_opp A). + -m is an upper bound for (set_opp A). Proof. Take A : (ℝ → Prop) and m : ℝ. Assume that (is_lower_bound A m) (i). - Expand the definition of upper bound. - That is, write the goal as (for all b : ℝ, (set_opp A b) ⇒ b ≤ -m). + We need to show that (for all b : ℝ, (set_opp A b) ⇒ b ≤ -m). Take b : ℝ. Assume that (set_opp A b). Define a := (-b). By (i) it holds that (m ≤ a). @@ -175,16 +179,13 @@ Qed. Lemma low_bd_set_opp_to_upp_bd_set : ∀ (A : ℝ → Prop) (m : ℝ), is_lower_bound (set_opp A) m ⇒ - -m is an upper bound of A. + -m is an upper bound for A. Proof. Take A : (ℝ → Prop) and m : ℝ. - Assume that (is_lower_bound (set_opp A) m) (i). + Assume that (is_lower_bound (set_opp A) m). We need to show that (∀ a : ℝ, (A a) ⇒ a ≤ -m). - Expand the definition of upper bound. - That is, write the goal as (for all a : ℝ, (A a) ⇒ a ≤ -m). Take a : ℝ. Assume that (A a). - Expand the definition of lower bound in (i). - That is, write (i) as (for all b : ℝ, (set_opp A b) ⇒ m ≤ b). + It holds that (for all b : ℝ, (set_opp A b) ⇒ m ≤ b). We claim that (A (--a)). { It holds that (--a = a) (ii). It holds that (A a) (iii). @@ -207,8 +208,6 @@ Proof. Take A : (ℝ → Prop) and M : ℝ. Assume that (is_upper_bound (set_opp A) M) (i). We need to show that (∀ a : ℝ, (A a) ⇒ -M ≤ a). - Expand the definition of is_lower_bound. - That is, write the goal as (for all a : ℝ, (A a) ⇒ -M ≤ a). Take a : ℝ. Assume that (A a). We claim that (A (--a)). { It holds that (--a = a) (ii). @@ -230,10 +229,8 @@ Proof. Take A : (ℝ → Prop). Assume that (is_bounded_below A) (i). We need to show that (∃ M : ℝ, is_upper_bound (set_opp A) M). - Expand the definition of is_bounded_below in (i). - That is, write (i) as (there exists m : ℝ, is_lower_bound A m). - Obtain m according to (i), so for m : R it holds that (is_lower_bound A m) (ii). - + By (i) it holds that (there exists m : ℝ, is_lower_bound A m). + Obtain such an m. Choose M := (-m). By low_bd_set_to_upp_bd_set_opp we conclude that (is_upper_bound (set_opp A) (M)). Qed. @@ -244,24 +241,19 @@ Lemma sup_set_opp_is_inf_set : is_sup (set_opp A) M ⇒ is_inf A (-M). Proof. Take A : (ℝ → Prop) and M : ℝ. - Assume that (is_sup (set_opp A) M) (i). - Expand the definition of is_lub in (i). - That is, write (i) as (is_upper_bound (set_opp A) M - ∧ (for all M0 : ℝ, is_upper_bound (set_opp A) M0 ⇨ M ≤ M0)). - Because (i) both (is_upper_bound (set_opp A) M) (ii) and - (for all M0 : ℝ, is_upper_bound (set_opp A) M0 ⇨ M ≤ M0) hold. - Expand the definition of is_inf. - That is, write the goal as + Assume that (is_sup (set_opp A) M). + It holds that (is_upper_bound (set_opp A) M + ∧ (for all M0 : ℝ, is_upper_bound (set_opp A) M0 ⇨ M ≤ M0)) (i). + Because (i) both (is_upper_bound (set_opp A) M) and + (for all M0 : ℝ, is_upper_bound (set_opp A) M0 ⇨ M ≤ M0) hold. + We need to show that (is_lower_bound A (- M) ∧ (for all l : ℝ, is_lower_bound A l ⇨ l ≤ -M)). We show both statements. - We need to show that (is_lower_bound A (- M)). We claim that (is_upper_bound (set_opp A) M). - Expand the definition of is_upper_bound. - That is, write the goal as (for all a : ℝ, (set_opp A a) ⇒ a ≤ M). + We need to show that (for all a : ℝ, (set_opp A a) ⇒ a ≤ M). Take a : ℝ. Assume that (set_opp A a). - - Expand the definition of is_upper_bound in (ii). - That is, write (ii) as (for all x : ℝ, (set_opp A x) ⇒ x ≤ M). + It holds that (for all x : ℝ, (set_opp A x) ⇒ x ≤ M) (ii). By (ii) it holds that (is_upper_bound (set_opp A) M). We conclude that (a <= M). @@ -270,12 +262,11 @@ Proof. - We need to show that (∀ l : ℝ, is_lower_bound A l ⇒ l ≤ -M). Take l : ℝ. Assume that (is_lower_bound A l). - Expand the definition of is_lub in (i). - That is, write (i) as (is_upper_bound (set_opp A) M + It holds that (is_upper_bound (set_opp A) M ∧ (for all M0 : ℝ, is_upper_bound (set_opp A) M0 ⇨ M ≤ M0)). - By (ii) it holds that (∀ b : ℝ, is_upper_bound (set_opp A) b ⇒ M ≤ b) (iii). + It holds that (∀ b : ℝ, is_upper_bound (set_opp A) b ⇒ M ≤ b) (ii). By low_bd_set_to_upp_bd_set_opp it holds that (is_upper_bound (set_opp A) (-l)). - By (iii) it holds that (M ≤ -l). + By (ii) it holds that (M ≤ -l). We conclude that (l ≤ - M). Qed. @@ -299,8 +290,8 @@ Proof. } It holds that ((set_opp A) (-z)) (iv). It holds that (B (-z)). - By R_complete it holds that (there exists M : ℝ, is_sup B M) (v). - Obtain M according to (v), so for M : R it holds that (is_sup B M). + By R_complete it holds that (there exists M : ℝ, is_sup B M). + Obtain such an M. Choose m := (- M). By sup_set_opp_is_inf_set we conclude that (is_inf A m). Qed. @@ -315,10 +306,9 @@ Lemma sup_is_upp_bd : Proof. Take A : (ℝ → Prop). Take M : ℝ. - Assume that (is_sup A M) (i). - Expand the definition of is_lub in (i). - That is, write (i) as (is_upper_bound A M - ∧ (for all b : ℝ, is_upper_bound A b ⇨ M ≤ b)). + Assume that (is_sup A M). + It holds that (is_upper_bound A M + ∧ (for all b : ℝ, is_upper_bound A b ⇨ M ≤ b)) (i). Because (i) both (is_upper_bound A M) and (for all M0 : ℝ, is_upper_bound A M0 ⇨ M ≤ M0) hold. It follows that (is_upper_bound A M). @@ -421,17 +411,15 @@ Proof. Take A : (ℝ → Prop) and M : ℝ. Assume that (∀ L : ℝ, L < M ⇒ there exists a : ℝ, (A a) ∧ L < a) (i). Take K : ℝ. -Assume that (is_upper_bound A K) (ii). -Expand the definition of is_upper_bound in (ii). -That is, write (ii) as - (∀ a : ℝ, (A a) ⇒ a ≤ K). +Assume that (is_upper_bound A K). +It holds that (∀ a : ℝ, (A a) ⇒ a ≤ K) (ii). We need to show that (M ≤ K). We argue by contradiction. Assume that (¬ M ≤ K). It holds that (M > K). -By (i) it holds that (∃ a : ℝ, (A a) ∧ K < a) (iii). -Obtain a according to (iii), so for a : ℝ it holds that ((A a) ∧ (K < a)) (iv). -Because (iv) both (A a) and (K < a) hold. +By (i) it holds that (∃ a : ℝ, (A a) ∧ K < a). +Obtain such an a. It holds that ((A a) ∧ (K < a)) (iii). +Because (iii) both (A a) and (K < a) hold. By (ii) it holds that (a ≤ K). It holds that (K < K). It holds that (¬ (K < K)). @@ -446,18 +434,16 @@ Proof. Take A : (ℝ → Prop) and m : ℝ. Assume that (∀ L : ℝ, L > m ⇒ there exists a : ℝ, (A a) ∧ L > a) (i). Take K : ℝ. -Assume that (is_lower_bound A K) (ii). -Expand the definition of is_lower_bound in (ii). -That is, write (ii) as - (∀ a : ℝ, (A a) ⇒ K ≤ a). +Assume that (is_lower_bound A K). +It holds that (∀ a : ℝ, (A a) ⇒ K ≤ a) (ii). We need to show that (K ≤ m). We argue by contradiction. Assume that (¬ K ≤ m). It holds that (K > m). -By (i) it holds that (∃ a : ℝ, (A a) ∧ K > a) (iii). -Obtain a according to (iii), so for a : ℝ it holds that ((A a) ∧ (K > a)) (iv). -Because (iv) both (A a) and (K > a) hold. -By ii it holds that (K ≤ a). +By (i) it holds that (∃ a : ℝ, (A a) ∧ K > a). +Obtain such an a. It holds that ((A a) ∧ (K > a)) (iii). +Because (iii) both (A a) and (K > a) hold. +By (ii) it holds that (K ≤ a). It holds that (K > K). It holds that (¬ (K > K)). Contradiction. @@ -475,9 +461,9 @@ Proof. It holds that (M - L > 0). Define ε1 := (M - L). It holds that (ε1 > 0). - By (i) it holds that (there exists a : ℝ, (A a) ∧ M - ε1 < a) (ii). - Obtain a according to (ii), so for a : ℝ it holds that ((A a) ∧ (M - ε1 < a)) (iii). - Because (iii) both (A a) and (M - ε1 < a) hold. + By (i) it holds that (there exists a : ℝ, (A a) ∧ M - ε1 < a). + Obtain such an a. It holds that ((A a) ∧ (M - ε1 < a)) (ii). + Because (ii) both (A a) and (M - ε1 < a) hold. Choose (a). We show both (A a) and (L < a). - We conclude that (A a). @@ -496,10 +482,10 @@ Proof. It holds that (L - m > 0). Define ε1 := (L - m). It holds that (ε1 > 0). - By (i) it holds that (there exists a : ℝ, (A a) ∧ m + ε1 > a) (ii). - Obtain a according to (ii), so for a : ℝ it holds that ((A a) ∧ (m + ε1 > a)) (iii). + By (i) it holds that (there exists a : ℝ, (A a) ∧ m + ε1 > a). + Obtain such an a. It holds that ((A a) ∧ (m + ε1 > a)) (ii). Choose (a). - Because (iii) both (A a) and (m + ε1 > a) hold. + Because (ii) both (A a) and (m + ε1 > a) hold. We show both (A a) and (L > a). - We conclude that (A a). - We conclude that (& a < m + ε1 = m + L - m = L). @@ -554,20 +540,18 @@ Proof. Take A : (ℝ → Prop) and M : ℝ. We show both directions. - We need to show that (is_sup A M ⇨ is_sup_alt_char A M). - Assume that (is_sup A M) (i). - Expand the definition of is_sup_alt_char. - That is, write the goal as ( + Assume that (is_sup A M). + We need to show that ( is_upper_bound A M ∧ (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ M - ε < a) ). We show both statements. + We need to show that (is_upper_bound A M). - Expand the definition of is_lub in (i). - That is, write (i) as ( + It holds that ( is_upper_bound A M ∧ (for all M0 : ℝ, - is_upper_bound A M0 ⇨ M ≤ M0) ). + is_upper_bound A M0 ⇨ M ≤ M0) ) (i). Because (i) both (is_upper_bound A M) and (for all M0 : ℝ, is_upper_bound A M0 ⇨ M ≤ M0) hold. It follows that (is_upper_bound A M). @@ -577,18 +561,16 @@ Proof. We conclude that (M is the supremum of A). - We need to show that (is_sup_alt_char A M ⇨ is_sup A M). - Assume that (is_sup_alt_char A M) (i). - Expand the definition of is_sup_alt_char in (i). - That is, write (i) as ( + Assume that (is_sup_alt_char A M). + It holds that ( is_upper_bound A M ∧ (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ - M - ε < a) ). + M - ε < a) ) (i). Because (i) both (is_upper_bound A M) (ii) and (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ M - ε < a) (iii) hold. - Expand the definition of is_lub. - That is, write the goal as ( + We need to show that ( is_upper_bound A M ∧ (for all M0 : ℝ, is_upper_bound A M0 ⇨ M ≤ M0) ). @@ -609,20 +591,18 @@ Proof. Take A : (ℝ → Prop) and m : ℝ. We show both directions. - We need to show that (is_inf A m ⇨ is_inf_alt_char A m). - Assume that (is_inf A m) (i). - Expand the definition of is_inf_alt_char. - That is, write the goal as ( + Assume that (is_inf A m). + We need to show that ( is_lower_bound A m ∧ (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ m + ε > a) ). We show both statements. + We need to show that (is_lower_bound A m). - Expand the definition of is_inf in (i). - That is, write (i) as ( + It holds that ( is_lower_bound A m ∧ (for all l : ℝ, is_lower_bound A l ⇨ l ≤ m) - ). + ) (i). Because (i) both (is_lower_bound A m) and (for all l : ℝ, is_lower_bound A l ⇨ l ≤ m) hold. It follows that (is_lower_bound A m). @@ -635,18 +615,16 @@ Proof. m + ε > a). - We need to show that (is_inf_alt_char A m ⇨ is_inf A m). - Assume that (is_inf_alt_char A m) (i). - Expand the definition of is_inf_alt_char in (i). - That is, write (i) as ( + Assume that (is_inf_alt_char A m). + It holds that ( is_lower_bound A m ∧ (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ - m + ε > a) ). + m + ε > a) ) (i). Because (i) both (is_lower_bound A m) (ii) and (for all ε : ℝ, ε > 0 ⇨ there exists a : ℝ, (A a) ∧ m + ε > a) hold. - Expand the definition of is_inf. - That is, write the goal as ( + We need to show that ( is_lower_bound A m ∧ (for all l : ℝ, is_lower_bound A l ⇨ l ≤ m) ). @@ -724,19 +702,16 @@ Lemma seq_ex_almost_maximizer_ε : Proof. Take a : (ℕ → ℝ). Assume that (has_ub a) (i). - Expand the definition of lub. - That is, write the goal as (for all ε : ℝ, ε > 0 + We need to show that (for all ε : ℝ, ε > 0 ⇨ there exists k : ℕ, a k > (let (a0, _) := ub_to_lub a (i) in a0) - ε). Define lub_a_prf := (ub_to_lub a (i)). - Obtain l according to (lub_a_prf), so for l : R it holds that (is_sup (EUn a) l). + clear _defeq. Obtain such an l. Take ε : ℝ; such that (ε > 0). - By exists_almost_maximizer_ε it holds that (∃ y : ℝ, (EUn a) y ∧ y > l - ε) (iv). - Obtain y according to (iv), so for y : R it holds that - ((EUn a) y ∧ y > l - ε) (v). - Because (v) both (EUn a y) (vi) and (y > l - ε) hold. - Expand the definition of EUn in (vi). - That is, write (vi) as (there exists n : ℕ , y = a n). - Obtain n according to (vi), so for n : nat it holds that (y = a n). + By exists_almost_maximizer_ε it holds that (∃ y : ℝ, (EUn a) y ∧ y > l - ε). + Obtain such a y. It holds that ((EUn a) y ∧ y > l - ε) (iv). + Because (iv) both (EUn a y) and (y > l - ε) hold. + It holds that (there exists n : ℕ , y = a n). + Obtain such an n. Choose k := n. We need to show that (l - ε < a n). We conclude that (& l - ε < y = a n). @@ -763,10 +738,8 @@ Proof. Assume that (has_ub a) (i). Take m, Nn : ℕ. By seq_ex_almost_maximizer_m it holds that - (∃ k : ℕ, a (Nn + k)%nat > sequence_ub a (i) Nn - 1 / (INR m + 1)) (ii). - Obtain k according to (ii), so for k : nat it holds that - (a (Nn + k)%nat > sequence_ub a i Nn - 1 / (m + 1)). - Choose l := (Nn+k)%nat. + (∃ k : ℕ, a (Nn + k)%nat > sequence_ub a (i) Nn - 1 / (INR m + 1)). + Obtain such a k. Choose l := (Nn+k)%nat. We show both statements. - We need to show that (l ≥ Nn)%nat. We conclude that (l ≥ Nn)%nat. @@ -787,4 +760,4 @@ Qed. #[export] Hint Unfold is_upper_bound : wp_reals. #[export] Hint Unfold is_lower_bound :reals. -Close Scope R_scope. +Close Scope R_scope. \ No newline at end of file diff --git a/theories/Libs/Logic/ConstructiveLogic.v b/theories/Libs/Logic/ConstructiveLogic.v new file mode 100644 index 00000000..e396f54f --- /dev/null +++ b/theories/Libs/Logic/ConstructiveLogic.v @@ -0,0 +1,130 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +(** ClassicalEpsilon allows us to lift an uninformative or to an informative or. *) + +Lemma make_sumbool_uninformative_1 : forall P Q : Prop, {P} + {Q} -> P \/ Q. +Proof. +intros P Q H. +destruct H. +- left. + exact p. +- right. + exact q. +Qed. + +Lemma make_sumbool_uninformative_2 : forall P Q : Prop, {Q} + {P} -> P \/ Q. +Proof. +intros P Q H. +destruct H. +- right. + exact q. +- left. + exact p. +Qed. + +Lemma make_sumtriad_uninformative_1 : forall P Q R: Prop, {P} + {Q} + {R} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | H2]. +- destruct H1. + * left. + exact p. + * right. + left. + exact q. +- right. + right. + exact H2. +Qed. + +Lemma make_sumtriad_uninformative_2 : forall P Q R: Prop, {P} + {R} + {Q} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | r]. +- destruct H1 as [p | q]. + * left. + exact p. + * right. + right. + exact q. +- right. + left. + exact r. +Qed. + +Lemma make_sumtriad_uninformative_3 : forall P Q R: Prop, {Q} + {P} + {R} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | r]. +- destruct H1. + * right. + left. + exact q. + * left. + exact p. +- right. + right. + exact r. +Qed. + +Lemma make_sumtriad_uninformative_4 : forall P Q R: Prop, {Q} + {R} + {P} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | p]. +- destruct H1 as [q | r]. + * right. + left. + exact q. + * right. + right. + exact r. +- left. + exact p. +Qed. + +Lemma make_sumtriad_uninformative_5 : forall P Q R: Prop, {R} + {P} + {Q} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | q]. +- destruct H1 as [r | p]. + * right. + right. + exact r. + * left. + exact p. +- right. + left. + exact q. +Qed. + +Lemma make_sumtriad_uninformative_6 : forall P Q R: Prop, {R} + {Q} + {P} -> P \/ Q \/ R. +Proof. +intros P Q R H. +destruct H as [H1 | p]. +- destruct H1 as [r | q]. + * right. + right. + exact r. + * right. + left. + exact q. +- left. + exact p. +Qed. + diff --git a/theories/Libs/Logic/InformativeEpsilon.v b/theories/Libs/Logic/InformativeEpsilon.v new file mode 100644 index 00000000..fa0c3fae --- /dev/null +++ b/theories/Libs/Logic/InformativeEpsilon.v @@ -0,0 +1,45 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import ClassicalEpsilon. + +(** ClassicalEpsilon allows us to lift an uninformative or to an informative or. *) + +Lemma informative_or_lift : forall P Q : Prop, P \/ Q -> {P} + {Q}. +Proof. +intros P Q. +assert ({P} + {~P}) as H by (apply excluded_middle_informative). +destruct H. +* intro. + left. + exact p. +* intros H. + assert ({Q} + {~Q}) as H2 by (apply excluded_middle_informative). + destruct H2. + + right. + exact q. + + assert (~~Q). + { + destruct H. + + contradiction. + + intro H1. + destruct H1. + exact H. + } + contradiction. +Qed. diff --git a/theories/Libs/Negation.v b/theories/Libs/Negation.v index 55758e0f..98ec47d0 100644 --- a/theories/Libs/Negation.v +++ b/theories/Libs/Negation.v @@ -194,8 +194,10 @@ Ltac2 solve_by_manipulating_negation_in (h_id : ident) := let attempt () := revert $h_id; repeat ( - first [ (* finish proof *) - exact id + first [ + (* finish directly if statements match *) + exact id + (* try all the different pattern matching to simplify goal *) | lazy_match! goal with (* without negation *) | [ |- (?a \/ ?b) -> (?c \/ ?d)] => apply (or_func $a $b $c $d) @@ -209,6 +211,7 @@ Ltac2 solve_by_manipulating_negation_in (h_id : ident) := apply (ex_func _ $p $q); let x_id := Fresh.in_goal @x in intro $x_id + (* with negation *) | [ |- ~(?a \/ ?b) -> (?c /\ ?d)] => apply (not_or_and_func $a $b $c $d) | [ |- (?c /\ ?d) -> ~(?a \/ ?b)] => apply (not_or_and_func $a $b $c $d) | [ |- ~(?a /\ ?b) -> (?c \/ ?d)] => apply (not_and_or_func $a $b $c $d) @@ -235,7 +238,7 @@ Ltac2 solve_by_manipulating_negation_in (h_id : ident) := intro $x_id | [ |- (~~?a) -> ?b] => apply (not_neg_pos_func $a $b) | [ |- ?b -> (~~?a)] => apply (pos_not_neg_func $a $b) - end + end ] ) in @@ -246,7 +249,7 @@ Ltac2 solve_by_manipulating_negation_in (h_id : ident) := end. -Ltac2 solve_by_manipulating_negation () := +Ltac2 solve_by_manipulating_negation (final_tac : unit -> unit) := match! goal with - | [ h : _ |- _ ] => solve_by_manipulating_negation_in h - end. \ No newline at end of file + | [ h : _ |- _ ] => progress (solve_by_manipulating_negation_in h; intro $h); final_tac () + end. diff --git a/theories/Libs/Reals.v b/theories/Libs/Reals.v index a7453eb9..3cf92e9c 100644 --- a/theories/Libs/Reals.v +++ b/theories/Libs/Reals.v @@ -218,4 +218,17 @@ Proof. Qed. Close Scope subset_scope. -Close Scope R_scope. \ No newline at end of file +Close Scope R_scope. + +Require Import Ltac2.Ltac2. + +(** Tactic to deal with Rabs Rmin Rmax *) + +Ltac2 crush_R_abs_min_max () := + unfold Rdist in *; + unfold Rabs in *; + unfold Rmax in *; + unfold Rmin in *; + repeat (destruct (Rcase_abs) in * ); + repeat (destruct (Rle_dec) in * ); + repeat (destruct (Rge_dec) in * ). diff --git a/theories/Notations/Common.v b/theories/Notations/Common.v index 7f9e6a05..0bc622dd 100644 --- a/theories/Notations/Common.v +++ b/theories/Notations/Common.v @@ -60,5 +60,5 @@ Notation "x ↔ y" := (x <-> y) (at level 95, no associativity): type_scope. Notation "x ⇔ y" := (x <-> y) (at level 95, no associativity): type_scope. Notation "¬ x" := (~x) (at level 75, right associativity) : type_scope. -Notation "'Show' 'a' 'contradiction' 'by:' '(1)' 'Showing' 'that' 'both' 'P' 'and' '¬P' 'hold' 'for' 'some' 'statement' 'P.' '(2)' 'Writing' '‘Contradiction.‘' 'or' '‘↯.‘.'" := (False) - (only printing, format "'[ ' Show a contradiction by: ']' '//' (1) Showing that both P and ¬P hold for some statement P. '//' (2) Writing ‘Contradiction.‘ or ‘↯.‘."). \ No newline at end of file +Notation "'Derive' 'a' 'contradiction.'" := (False) + (only printing). \ No newline at end of file diff --git a/theories/Tactics.v b/theories/Tactics.v index fe083d42..65703147 100644 --- a/theories/Tactics.v +++ b/theories/Tactics.v @@ -24,6 +24,7 @@ Require Export Tactics.BothDirections. Require Export Tactics.BothStatements. Require Export Tactics.Claims. Require Export Tactics.Choose. +Require Export Tactics.Obtain. Require Export Tactics.Conclusion. Require Export Tactics.Contradiction. Require Export Tactics.Define. diff --git a/theories/Tactics/Assume.v b/theories/Tactics/Assume.v index a925a456..7f8bb28e 100644 --- a/theories/Tactics/Assume.v +++ b/theories/Tactics/Assume.v @@ -18,19 +18,18 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Constr. Require Import Util.Goals. Require Import Util.Hypothesis. Require Import Util.Init. - -Ltac2 Type exn ::= [ AssumeError(message) ]. +Require Import Util.MessagesToUser. Local Ltac2 expected_of_type_instead_of_message (e : constr) (t : constr) := - concat (concat - (concat (of_string "Expected assumption of ") (of_constr e)) - (concat (of_string " instead of ") (of_constr t))) (of_string "."). - + concat_list [of_string "Expected assumption of "; of_constr e; + of_string " instead of "; of_constr t; of_string "."]. (** Attempts to assume a negated expression. @@ -39,9 +38,9 @@ Local Ltac2 expected_of_type_instead_of_message (e : constr) (t : constr) := Does: - For the first pair of (expession (and name)) in [x], assume the expression. - Raises Exceptions: - - [AssumeError], if the current goal does not require the assumption of an expression [t] where [t] is the expression from the first pair in [x]. - - [AssumeError], if [x] contains more than one element. + Raises fatal exceptions: + - If the current goal does not require the assumption of an expression [t] where [t] is the expression from the first pair in [x]. + - If [x] contains more than one element. *) Local Ltac2 assume_negation (x : (constr * (ident option)) list) := match x with @@ -51,14 +50,14 @@ Local Ltac2 assume_negation (x : (constr * (ident option)) list) := lazy_match! goal with | [ |- not ?u ] => match check_constr_equal u t with - | false => Control.zero (AssumeError (expected_of_type_instead_of_message u t)) + | false => throw (expected_of_type_instead_of_message u t) | true => (* Check whether this was the only assumption made.*) match tail with - | h::t => Control.zero (AssumeError (of_string "Nothing left to assume after the negated expression.")) + | h::t => throw (of_string "Nothing left to assume after the negated expression.") | [] => (* Assume negation : check whether a name has been given *) match n with - | None => let h := Fresh.in_goal @__wp__h in intro $h - | Some n => intro $n + | None => let h := Fresh.in_goal @_H in intro $h; change $t in $h + | Some n => intro $n; change $t in $n end end end @@ -77,8 +76,8 @@ end. - For the first pair of (hypothesis (and name)) in [x], assume the hypothesis (with specified name). If the assumed hypothesis did not come from a negated expression, proceeds to call itself with the remaining pairs in [x] as a new list [x']. - Raises Exceptions: - - [AssumeError], if the current goal does not require the assumption of any more hypotheses in general or one of type [t], where [t] is the type from the first pair in [x]. + Raises fatale xceptions: + - If the current goal does not require the assumption of any more hypotheses in general or one of type [t], where [t] is the type from the first pair in [x]. *) Local Ltac2 rec process_ident_type_pairs (x : (constr * (ident option)) list) := match x with @@ -94,14 +93,14 @@ Local Ltac2 rec process_ident_type_pairs (x : (constr * (ident option)) list) := match check_constr_equal u t with | true => (* Check whether a name has been given *) match n with - | None => let h := Fresh.in_goal @__wp__h in intro $h - | Some n => intro $n + | None => let h := Fresh.in_goal @_H in intro $h; change $t in $h + | Some n => intro $n; change $t in $n end - | false => Control.zero (AssumeError (expected_of_type_instead_of_message u t)) + | false => throw (expected_of_type_instead_of_message u t) end - | false => Control.zero (AssumeError (of_string "[Assume] cannot be used to construct a map (→). Use [Take] instead.")) + | false => throw (of_string "`Assume ...` cannot be used to construct a map (→). Use [Take] instead.") end - | [ |- _ ] => Control.zero (AssumeError (of_string "Tried to assume too many properties.")) + | [ |- _ ] => throw (of_string "Tried to assume too many properties.") end end; @@ -114,7 +113,7 @@ Local Ltac2 rec process_ident_type_pairs (x : (constr * (ident option)) list) := Local Ltac2 remove_contra_wrapper (wrapped_assumption : constr) (assumption : constr) := match (check_constr_equal wrapped_assumption assumption) with | true => apply (ByContradiction.wrap $wrapped_assumption) - | false => Control.zero (AssumeError (of_string "Wrong assumption specified.")) + | false => throw (of_string "Wrong assumption specified.") end. @@ -138,7 +137,7 @@ Local Ltac2 assume (x : (constr * (ident option)) list) := lazy_match! goal with | [ |- not _ ] => assume_negation x | [ |- _ -> _ ] => process_ident_type_pairs x - | [ |- _ ] => Control.zero (AssumeError (of_string "[Assume] can only be used to prove an implication (⇨) or a negation (¬).")) + | [ |- _ ] => throw (of_string "`Assume ...` can only be used to prove an implication (⇨) or a negation (¬).") end. diff --git a/theories/Tactics/Because.v b/theories/Tactics/Because.v index c5b3f385..a7dcbc9a 100644 --- a/theories/Tactics/Because.v +++ b/theories/Tactics/Because.v @@ -18,17 +18,19 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := +List.fold_right concat ls (of_string ""). Require Import Util.Goals. Require Import Util.Hypothesis. +Require Import Util.MessagesToUser. -Local Ltac2 warn_wrong_prop_specified (user_type:constr) (coq_type:constr) := +Local Ltac2 check_wrong_prop_specified (user_type:constr) (coq_type:constr) := match Constr.equal user_type coq_type with | true => () - | false => - Control.zero - (InputError (concat (concat (concat (of_string "Property ") (of_constr user_type)) - (concat (of_string " should be ") (of_constr coq_type))) (of_string "."))) + | false => throw (concat_list + [of_string "Property "; of_constr user_type; of_string " should be "; + of_constr coq_type; of_string "."]) end. (** @@ -44,8 +46,8 @@ Local Ltac2 warn_wrong_prop_specified (user_type:constr) (coq_type:constr) := Does: - splits [s] into its two respective parts. - Raises Exceptions: - - [InputError] if the specified type [tu] or [tv] is not actually the type of [u] or [v] resp. + Raises fatal exceptions: + - If the specified type [tu] or [tv] is not actually the type of [u] or [v] resp. *) Local Ltac2 and_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:constr) (v:ident option) (tv:constr) := @@ -56,12 +58,12 @@ Local Ltac2 and_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:co let copy_val := Control.hyp copy in (* Create identifiers if not specified. *) let uu := match u with - | None => Fresh.in_goal @__wp__hu + | None => Fresh.in_goal @_Ha | Some u => u end in let vv := match v with - | None => Fresh.in_goal @__wp__hv + | None => Fresh.in_goal @_Hb | Some v => v end in @@ -70,10 +72,10 @@ Local Ltac2 and_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:co destruct $copy_val as [$uu $vv]; let type_u := get_value_of_hyp_id uu in - warn_wrong_prop_specified tu type_u; + check_wrong_prop_specified tu type_u; let type_v := get_value_of_hyp_id vv in - warn_wrong_prop_specified tv type_v. + check_wrong_prop_specified tv type_v. Ltac2 Notation "Because" "(" s(ident) ")" "both" tu(constr) u(opt(seq("(", ident, ")"))) "and" tv(constr) v(opt(seq("(", ident, ")"))) w(opt("hold")) := @@ -94,8 +96,8 @@ Ltac2 Notation "Because" "(" s(ident) ")" "both" tu(constr) u(opt(seq("(", ident Does: - splits [s] into its two respective parts. Wraps the goal for both parts in the [Case.Wrapper] wrapper. - Raises Exceptions: - - [InputError] if the specified type [tu] or [tv] is not actually the type of [u] or [v] resp. + Raises fatal exceptions: + - If the specified type [tu] or [tv] is not actually the type of [u] or [v] resp. *) Local Ltac2 or_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:constr) (v:ident option) (tv: constr) := (* Copy hypothesis we will destruct. *) @@ -106,12 +108,12 @@ Local Ltac2 or_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:con let copy_val := Control.hyp copy in (* Create identifiers if not specified. *) let uu := match u with - | None => Fresh.in_goal @__wp__hu + | None => Fresh.in_goal @_Ha | Some u => u end in let vv := match v with - | None => Fresh.in_goal @__wp__hv + | None => Fresh.in_goal @_Hb | Some v => v end in @@ -120,13 +122,13 @@ Local Ltac2 or_hypothesis_destruct_with_types (s:ident) (u:ident option) (tu:con destruct $copy_val as [$uu | $vv]; Control.focus 1 1 (fun () => let type_u := get_value_of_hyp_id uu in - warn_wrong_prop_specified tu type_u; + check_wrong_prop_specified tu type_u; apply (Case.unwrap $type_u) ); Control.focus 2 2 (fun () => let type_v := get_value_of_hyp_id vv in - warn_wrong_prop_specified tv type_v; + check_wrong_prop_specified tv type_v; apply (Case.unwrap $type_v) ). diff --git a/theories/Tactics/BothDirections.v b/theories/Tactics/BothDirections.v index 57c2a949..e35ce991 100644 --- a/theories/Tactics/BothDirections.v +++ b/theories/Tactics/BothDirections.v @@ -19,8 +19,8 @@ Require Import Ltac2.Ltac2. Require Export Util.Goals. +Require Import Util.MessagesToUser. -Ltac2 Type exn ::= [ BothDirectionsError(string) ]. (** Split the proof of an if and only if statement into both of its directions, wraps both resulting goals in a [StateGoal.Wrapper]. @@ -32,13 +32,13 @@ Ltac2 Type exn ::= [ BothDirectionsError(string) ]. - splits the if and only if statement into its both directions. - wraps both goals in a [StateGoal.Wrapper]. - Raises Exceptions: - - [BothDirectionsError], if the [goal] is not an if and only if [goal]. + Raises fatal exceptions: + - If the [goal] is not an if and only if [goal]. *) Ltac2 both_statements_iff () := lazy_match! goal with | [ |- _ <-> _] => split; Control.enter (fun () => apply StateGoal.unwrap) - | [ |- _ ] => Control.zero (BothDirectionsError "The goal is not to show an `if and only if`-statement, try another tactic.") + | [ |- _ ] => throw (Message.of_string "The goal is not to show an `if and only if`-statement, try another approach.") end. Ltac2 Notation "We" "show" "both" "directions" := diff --git a/theories/Tactics/BothStatements.v b/theories/Tactics/BothStatements.v index a5e204c9..1de411fc 100644 --- a/theories/Tactics/BothStatements.v +++ b/theories/Tactics/BothStatements.v @@ -18,11 +18,12 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Constr. Require Import Util.Goals. - -Ltac2 Type exn ::= [ BothStatementsError(string) | InputError(message) ]. +Require Import Util.MessagesToUser. (** Split the proof of a conjuction statement into both of its parts, wraps both the resulting goals in a [StateGoal.Wrapper]. @@ -34,13 +35,13 @@ Ltac2 Type exn ::= [ BothStatementsError(string) | InputError(message) ]. - splits the conjunction statement into its both parts. - wraps both goals in a [StateGoal.Wrapper]. - Raises Exceptions: - - [BothStatementsError], if the [goal] is not a conjunction of statments. + Raises fatal exceptions: + - If the [goal] is not a conjunction of statments. *) Ltac2 both_directions_and () := lazy_match! goal with | [ |- _ /\ _] => split; Control.enter (fun () => apply StateGoal.unwrap) - | [ |- _ ] => Control.zero (BothStatementsError "This is not an 'and' statement, so try another tactic.") + | [ |- _ ] => throw (of_string "This is not an 'and' statement, so try another approach.") end. Ltac2 Notation "We" "show" "both" "statements" := @@ -52,9 +53,8 @@ Ltac2 Notation "We" "prove" "both" "statements" := both_directions_and (). Local Ltac2 need_to_show_instead_of_msg (correct:constr) (wrong:constr) := - concat (concat (concat (of_string "You need to show ") (of_constr correct)) - (concat (of_string " instead of ") (of_constr wrong))) (of_string "."). - + concat_list [of_string "You need to show "; of_constr correct; + of_string " instead of "; of_constr wrong; of_string "."]. (** Split the proof of a conjuction statement into two specified parts, but also verifies that the parts wrote by the user, in which the goal should split into, are the correct ones. @@ -67,8 +67,8 @@ Local Ltac2 need_to_show_instead_of_msg (correct:constr) (wrong:constr) := - If it cannot be written, it prints a statement saying what the respective part that does not match should actually be. - If the goal is of the form [t /\ s], it is changed to [s /\ t] before splitting. - Raises Exceptions: - - [BothStatementsError], if the [goal] is not a conjunction of the specified statments. + Raises fatal exceptions: + - If the [goal] is not a conjunction of the specified statments. *) Ltac2 both_directions_and_with_types (s: constr) (t:constr) := lazy_match! goal with @@ -77,7 +77,7 @@ Ltac2 both_directions_and_with_types (s: constr) (t:constr) := | true => match check_constr_equal t v with | true => split - | false => Control.zero (InputError (need_to_show_instead_of_msg v t)) + | false => throw (need_to_show_instead_of_msg v t) end | false => (* Otherwise, check if it matches the second part *) match check_constr_equal s v with @@ -86,20 +86,20 @@ Ltac2 both_directions_and_with_types (s: constr) (t:constr) := | true => apply and_comm; (* i.e. switch order *) split - | false => Control.zero (InputError (need_to_show_instead_of_msg u t)) + | false => throw (need_to_show_instead_of_msg u t) end | false => (* If s does not match anything, check if t matches something *) match check_constr_equal t u with - | true => Control.zero (InputError (need_to_show_instead_of_msg v s)) + | true => throw (need_to_show_instead_of_msg v s) | false => match check_constr_equal t v with - | true => Control.zero (InputError (need_to_show_instead_of_msg u s)) - | false => Control.zero (InputError (of_string "Neiher of these two statements are what you need to show.")) + | true => throw (need_to_show_instead_of_msg u s) + | false => throw (of_string "Neiher of these two statements are what you need to show.") end end end end - | [ |- _ ] => Control.zero (BothStatementsError "This is not an 'and' statement, so try another tactic.") + | [ |- _ ] => throw (of_string "This is not an 'and' statement, so try another tactic.") end. diff --git a/theories/Tactics/Choose.v b/theories/Tactics/Choose.v index 41af989e..7fbb0270 100644 --- a/theories/Tactics/Choose.v +++ b/theories/Tactics/Choose.v @@ -20,8 +20,9 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. Require Import Util.Goals. +Require Import Util.MessagesToUser. -Ltac2 Type exn ::= [ ChooseError(string) | ChooseSuchThatError(message) ]. +(* Ltac2 Type exn ::= [ ChooseError(string) ]. *) (** * Choose *) @@ -37,18 +38,18 @@ Ltac2 Type exn ::= [ ChooseError(string) | ChooseSuchThatError(message) ]. Does: - instantiates the [constr] [t] under the name [s]. - Raises Exceptions: - - [ChooseError], if the [goal] is not an [exists] [goal]. + Raises fatal exceptions: + - If the [goal] is not an [exists] [goal]. *) Ltac2 choose_variable_in_exists_goal_with_renaming (s:ident) (t:constr) := lazy_match! goal with | [ |- exists _ : _, _] => pose ($s := $t); let v := Control.hyp s in - let w := Fresh.fresh (Fresh.Free.of_goal ()) @add_eq in + let w := Fresh.fresh (Fresh.Free.of_goal ()) @_defeq in exists $v; assert ($w : $v = $t) by reflexivity - | [ |- _ ] => Control.zero (ChooseError "`Choose` can only be applied to 'exists' goals") + | [ |- _ ] => throw (of_string "`Choose` can only be applied to 'exists' goals.") end. @@ -62,13 +63,13 @@ Ltac2 choose_variable_in_exists_goal_with_renaming (s:ident) (t:constr) := Does: - instantiates the [constr] [t] under the same name. - Raises Exceptions: - - [ChooseError], if the [goal] is not an [exists] [goal]. + Raises fatal exceptions: + - If the [goal] is not an [exists] [goal]. *) Ltac2 choose_variable_in_exists_no_renaming (t:constr) := lazy_match! goal with | [ |- exists _ : _, _] => exists $t - | [ |- _ ] => Control.zero (ChooseError "`Choose` can only be applied to 'exists' goals") + | [ |- _ ] => throw (of_string "`Choose` can only be applied to 'exists' goals.") end. Ltac2 Notation "Choose" s(opt(seq(ident, ":="))) t(constr) := @@ -78,81 +79,3 @@ Ltac2 Notation "Choose" s(opt(seq(ident, ":="))) t(constr) := | Some s => choose_variable_in_exists_goal_with_renaming s t end. - -(** * Choose such that *) - -Local Ltac2 mismatch_pred_existential_message (s : ident) (v : ident) := - concat (concat - (concat (of_string "Claimed property of ") (of_ident s)) - (concat (of_string " does not match ‘there exists’-statement (") (of_ident v))) (of_string ")."). - - -(** * - Chooses a variable according to a particular hypothesis and labels the remaining parts of the definition. - - Arguments: - - [s: ident], the name of the variable to be chosen. - - [v: ident], the hypothesis used. - - [pred_u: constr], predicate that should hold for [s] and [v]'s type should match (ex [pred_u]) or (sig [pred_u]). - - [u: ident option], optional name of property that [s] is to satisfy. - Does: - - Destructs the constr [v] under the names [s] (and [u]). - - Copies the hypothesis [v] to a new hypothesis also called [v], hence the hypothesis is preserved despite its destruction. - - Raises exceptions: - - [ChooseSuchThatError], if [v]'s type does not match (ex [pred_u]) or (sig [pred_u]). -*) - -Ltac2 choose_such_that (s:ident) (v:ident) (pred_u:constr) (u:ident option) := - let v_val := Control.hyp v in - let copy_id := Fresh.in_goal @copy in - (* Create identifier if u is not specified. *) - let uu := match u with - | None => Fresh.in_goal @__wp__h - | Some u => u - end in - match Control.case (fun () => - (* Copy v, also count as check that v can be converted to (ex pred_u) *) - assert (ex $pred_u) as $copy_id; - Control.focus 1 1 (fun () => exact $v_val) - ) with - | Val _ => - (* ~continue with (v = ex pred_u) case~ *) - (* Destruct v *) - destruct $v_val as [$s $uu]; - (* Copy the copy, but with name v*) - assert (ex $pred_u) as $v; - Control.focus 1 1 (fun () => - let copy_val := Control.hyp copy_id in exact $copy_val); - (* Destroy copy *) - clear $copy_id - | Err e => - Control.plus - (fun () => - (* Copy v, also count as check that v can be converted to (sig pred_u) *) - assert (sig $pred_u) as $copy_id; - Control.focus 1 1 (fun () => exact $v_val) - ) (fun e => - Control.zero (ChooseSuchThatError (mismatch_pred_existential_message s v)) - ); - (* ~continue with (v = sig pred_u) case~ *) - (* Destruct v *) - destruct $v_val as [$s $uu]; - (* Copy the copy, but with name v*) - assert (sig $pred_u) as $v; - Control.focus 1 1 (fun () => - let copy_val := Control.hyp copy_id in exact $copy_val); - (* Destroy copy *) - clear $copy_id - end. - - -(* Desired syntax: - Choose x according to (i), so for x : A it holds that (P x) (ii). *) - -Notation "'for' x : A 'it' 'holds' 'that' p" := (fun x : A => p) (at level 1, x name, only parsing). - -Ltac2 Notation "Obtain" s(ident) "according" "to" "("v(ident)")" "," "so" pred_u(constr) u(opt(seq("(", ident, ")"))) := - panic_if_goal_wrapped (); - choose_such_that s v pred_u u. - diff --git a/theories/Tactics/Claims.v b/theories/Tactics/Claims.v index 53274b2d..10d8a67f 100644 --- a/theories/Tactics/Claims.v +++ b/theories/Tactics/Claims.v @@ -24,7 +24,7 @@ Require Import Util.Goals. Local Ltac2 my_assert (t:constr) (id:ident option) := match id with | None => - let h := Fresh.in_goal @__wp__h in + let h := Fresh.in_goal @_H in ltac2_assert h t | Some id => ltac2_assert id t end. diff --git a/theories/Tactics/Conclusion.v b/theories/Tactics/Conclusion.v index 19de950e..0954fbef 100644 --- a/theories/Tactics/Conclusion.v +++ b/theories/Tactics/Conclusion.v @@ -17,39 +17,27 @@ (******************************************************************************) Require Import Ltac2.Ltac2. - Require Import Ltac2.Message. Require Import Chains.Inequalities. Require Import Util.Goals. Require Import Util.Init. +Require Import Util.Since. Require Import Waterprove. +Require Import MessagesToUser. -Ltac2 Type exn ::= [ AutomationFailure(message) ]. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Ltac2 warn_equivalent_goal_given () := - print (of_string -"Warning: -The statement you provided does not exactly correspond to what you need to show. -This can make your proof less readable. -Waterproof will try to rewrite the goal..." + warn (of_string +"The statement you provided does not exactly correspond to what you need to show. +This can make your proof less readable." ). -Ltac2 warn_wrong_goal_given (wrong_target: constr) := - print - (concat - (concat - (concat - (of_string "The actual goal (") - (of_constr (Control.goal ())) - ) - (concat - (of_string ") is not equivalent to the goal you gave (") - (of_constr wrong_target) - ) - ) - (of_string "). ") - ). +Ltac2 wrong_goal_msg (wrong_goal : constr) := + concat_list + [of_constr wrong_goal; of_string " does not correspond to what you need to show."]. (** Check if [target] is judgementally (i.e. by rewriting definitions) equal to the goal. @@ -60,67 +48,95 @@ Ltac2 warn_wrong_goal_given (wrong_target: constr) := Returns: - [bool]: indicates if [target] is judgementally equal to the goal under focus. *) -Local Ltac2 target_equals_goal_judgementally (target:constr) := +Local Ltac2 target_equals_goal_judgementally (target : constr) := let target := eval cbv in $target in let real_goal := Control.goal () in - let real_goal := eval cbv in $real_goal in + let real_goal := eval cbv in $real_goal in Constr.equal target real_goal. + (** - Check if target_goal is what needs to be proven judgementally -- using global or weak global statement for inequality chains -- and attempts to solve with optional lemma. + Check if stated goal is what needs to be proven judgementally. + If so changes current goal into stated goal. + Uses global or weak global statement inequality chains of to compare + these to the current goal. Arguments: - - [target_goal: constr], expression that should equal the goal under focus. - - [lemma: constr option], optional lemma to include in the automatic proof completion ([waterprove]). + - [sttd_goal: constr], stated goal, the expression that should equal the goal under focus. - Raises exceptions: - - [AutomationFailure], if [waterprove] fails the prove the goal (i.e. the goal is too difficult, or does not hold). - - [AutomationFailure], if [target_goal] is not equivalent to the actual goal under focus, even after rewriting. + Raises fatal exceptions: + - If [sttd_goal] is not equivalent to the actual goal under focus, even after rewriting. *) -Ltac2 check_and_solve (target_goal:constr) (lemma_opt: constr option) := - (* First check if the given target equals the goal directly, - without applying any rewrite. *) - match Constr.equal target_goal (Control.goal ()) with - | false => - lazy_match! target_goal with - (* Do somethign special for inequality chains *) - | (total_statement ?u) => (* Convert inequality chain to global statement. *) - let new_target := constr:(global_statement $u) in - match target_equals_goal_judgementally new_target with - | false => - (* If at first no match, try to use weak global statement *) - let new_new_target := constr:(weak_global_statement $u) in - match target_equals_goal_judgementally new_new_target with - | false => - warn_wrong_goal_given (new_target); - Control.zero (AutomationFailure (of_string "Given goal not equivalent to actual goal.")) - | true => () - end - | true => () - end; - match lemma_opt with - | None => (enough $target_goal by (waterprove 5 false [] Main)) - | Some lem => (enough $target_goal by (rwaterprove 5 false [fun () => lem] Main [lem] [])) - end - | _ => - match target_equals_goal_judgementally target_goal with - | false => - warn_wrong_goal_given (target_goal); - Control.zero (AutomationFailure (of_string "Given goal not equivalent to actual goal.")) - | true => - (* User provided an equivalent goal, but written differently. - Try to rewrite the real goal to match user input.*) - warn_equivalent_goal_given (); - change $target_goal - end + + +Local Ltac2 guarantee_stated_goal_matches (sttd_goal : constr) := + (* Check if stated goal exactly matches current goal. *) + match Constr.equal sttd_goal (Control.goal ()) with + | true => () + | false => + (* If not, do some additional checks. *) + (* For inequality chains, consider the global statement. *) + lazy_match! sttd_goal with + | total_statement ?u => + (* Check if global statement matches judgementally. *) + let glob_statement := constr:(global_statement $u) in + match target_equals_goal_judgementally glob_statement with + | true => () + | false => + (* If not, try weak global statement. *) + let weak_glob_statement := constr:(weak_global_statement $u) in + match target_equals_goal_judgementally weak_glob_statement with + | true => () + | false => throw (wrong_goal_msg sttd_goal) + end + end; + (* Convert current goal to the given inequality chain.*) + enough $sttd_goal by (waterprove 5 false Main) + (* For the rest, just check for judgemental equality. *) + | _ => + match target_equals_goal_judgementally sttd_goal with + | true => warn_equivalent_goal_given (); change $sttd_goal + | false => throw (wrong_goal_msg sttd_goal) end - | true => () - end; - match lemma_opt with - | None => waterprove 5 true [] Main - | Some lem => rwaterprove 5 true [fun () => lem] Main [lem] [] + end end. +(** Attempts to solve current goal. *) +Local Ltac2 conclude () := + let err_msg (g : constr) := concat_list + [of_string "Could not verify that "; of_constr g; of_string "."] + in + match Control.case (fun () => waterprove 5 true Main) with + | Val _ => () + | Err (FailedToProve g) => throw (err_msg g) + | Err exn => Control.zero exn + end. + +(** Attempts to solve current goal using additional lemma which has to be used. *) +Local Ltac2 core_conclude_by (xtr_lemma : constr) := + let err_msg (g : constr) := concat_list + [of_string "Could not verify that "; of_constr g; of_string "."] + in + match Control.case (fun () => + rwaterprove 5 true Main xtr_lemma) + with + | Val _ => () + | Err (FailedToProve g) => throw (err_msg g) + | Err exn => Control.zero exn (* includes FailedToUse error *) + end. + +(** Adaptation of [core_conclude_by] that turns the [FailedToUse] errors + which might be thrown into user readable errors. *) +Local Ltac2 conclude_by (xtr_lemma : constr) := + wrapper_core_by_tactic core_conclude_by xtr_lemma. + +(** Adaptation of [core_conclude_by] that allows user to use mathematical statements themselves + instead of references to them as extra information for the automation system. + Uses the code in [Since.v]. *) +Local Ltac2 conclude_since (xtr_claim : constr) := + since_framework core_conclude_by xtr_claim. + + (** Removes a [StateGoal.Wrapper] wrapper from the goal. @@ -142,36 +158,45 @@ Local Ltac2 unwrap_state_goal_no_check () := Arguments: - [target_goal: constr], expression that should equal the goal under focus. - - [lemma: constr option], optional lemma to include in the automatic proof completion ([waterprove]). Raises exceptions: - [AutomationFailure], if [waterprove] fails the prove the goal (i.e. the goal is too difficult, or does not hold). - - [AutomationFailure], if [target_goal] is not equivalent to the actual goal under focus, even after rewriting. + - [ConcludeError], if [target_goal] is not equivalent to the actual goal under focus, even after rewriting. *) -Ltac2 Notation "We" "conclude" "that" target_goal(constr) := +Ltac2 Notation "We" "conclude" tht(opt("that")) target_goal(constr) := unwrap_state_goal_no_check (); panic_if_goal_wrapped (); - check_and_solve target_goal None. + guarantee_stated_goal_matches target_goal; + conclude (). (** Alternative notation for [We conclude that ...]. *) -Ltac2 Notation "It" "follows" "that" target_goal(constr) := +Ltac2 Notation "It" "follows" tht(opt("that")) target_goal(constr) := unwrap_state_goal_no_check (); panic_if_goal_wrapped (); - check_and_solve target_goal None. + guarantee_stated_goal_matches target_goal; + conclude (). (** Finish proving a goal using automation. Arguments: - [target_goal: constr], expression that should equal the goal under focus. + - [xtr_lemma: constr], lemma that can be and has to be used for proof of [target_goal]. Raises exceptions: - [AutomationFailure], if [waterprove] fails the prove the goal (i.e. the goal is too difficult, or does not hold). - - [AutomationFailure], if [target_goal] is not equivalent to the actual goal under focus, even after rewriting. + - [ConcludeError], if [target_goal] is not equivalent to the actual goal under focus, even after rewriting. *) -Ltac2 Notation "By" lemma(constr) "we" "conclude" "that" target_goal(constr) := +Ltac2 Notation "By" xtr_lemma(constr) "we" "conclude" tht(opt("that")) target_goal(constr) := + unwrap_state_goal_no_check (); + panic_if_goal_wrapped (); + guarantee_stated_goal_matches target_goal; + conclude_by xtr_lemma. + +Ltac2 Notation "Since" xtr_claim(constr) "we" "conclude" tht(opt("that")) target_goal(constr) := unwrap_state_goal_no_check (); panic_if_goal_wrapped (); - check_and_solve target_goal (Some lemma). + guarantee_stated_goal_matches target_goal; + conclude_since xtr_claim. diff --git a/theories/Tactics/Contradiction.v b/theories/Tactics/Contradiction.v index 2f4e6b13..fa3a2b13 100644 --- a/theories/Tactics/Contradiction.v +++ b/theories/Tactics/Contradiction.v @@ -19,8 +19,16 @@ Require Import Classical. Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). + +Require Import Util.Init. +Local Ltac2 get_type (x: constr) : constr := eval unfold type_of in (type_of $x). Require Import Util.Goals. +Require Import Util.MessagesToUser. +Require Import Waterprove. + (** Starts a proof by contradiction. @@ -36,23 +44,74 @@ Ltac2 contra () := | [ |- ?x] => apply (NNPP $x); apply (ByContradiction.unwrap (not $x)) - | [ |- _] => print(of_string "Failed to start a proof by contradiction.") end. (** - Calls the Ltac1 [contradiction] tactic. + Attempts to solve the goal by finding a contradiction to the previous statement. Arguments: - no arguments. Does: - - calls the Ltac1 [contradiction] tactic, as this tactic does not exist in Ltac2. Tries to find contradictory hypotheses to show the goal. + - If last hypothesis is of the form ~P, tries to find a proof of P and finish the + proof with the resulting contradiction. + - If last hypothesis is of the form P, tries to find a proof of ~P and finish the + proof with the resulting contradiction. + - Throws an error if no hypohteses, or if last hypothesis cannot be negated. *) -Ltac2 contradiction () := ltac1:(contradiction). + +Ltac2 contradiction () := + lazy_match! goal with + | [ id_h : _ |- _ ] => + let h := Control.hyp id_h in + let prop_h := get_type h in + let id_contra := Fresh.in_goal @_Hcontra in + lazy_match! prop_h with + | ~ ?p => + (* Try to find a proof of p *) + match Control.case (fun () => + assert $p as $id_contra by + (waterprove 5 true Main)) + with + | Err (FailedToProve g) => throw (concat_list + [of_string "Could not verify that "; of_constr g; of_string "."]) + | Err exn => Control.zero exn + | Val _ => + let p := Control.hyp id_contra in + apply False_rect; + exact ($h $p) + end + | ?p => + (* Try to find a proof of ~p *) + match Control.case (fun () => + assert (~ $p) as $id_contra) + with + | Err exn => throw (concat_list + [of_string "Previous statement cannot be negated."]) + | Val _ => + match Control.case (fun () => Control.focus 1 1 (fun () => + (waterprove 5 true Main))) + with + | Err (FailedToProve g) => throw (concat_list + [of_string "Could not verify that "; of_constr g; of_string "."]) + | Err exn => Control.zero exn + | Val _ => + let not_p := Control.hyp id_contra in + apply False_rect; + exact ($not_p $h) + end + end + end + | [ |- _ ] => throw (of_string "No statement to contradict.") + end. Ltac2 Notation "We" "argue" "by" "contradiction" := contra (). -Ltac2 Notation "Contradiction" := contradiction (). +Ltac2 Notation "Contradiction" := + panic_if_goal_wrapped (); + contradiction (). -Ltac2 Notation "↯" := contradiction (). \ No newline at end of file +Ltac2 Notation "↯" := + panic_if_goal_wrapped (); + contradiction (). \ No newline at end of file diff --git a/theories/Tactics/Define.v b/theories/Tactics/Define.v index bc682cd5..1f298cfc 100644 --- a/theories/Tactics/Define.v +++ b/theories/Tactics/Define.v @@ -33,7 +33,7 @@ Require Import Util.Goals. Local Ltac2 defining (u: ident) (t: constr) := set ($u := $t); let u_constr := Control.hyp u in - let w := Fresh.fresh (Fresh.Free.of_goal ()) @add_eq in + let w := Fresh.fresh (Fresh.Free.of_goal ()) @_defeq in assert ($w : $u_constr = $t) by reflexivity. diff --git a/theories/Tactics/Either.v b/theories/Tactics/Either.v index f4be8ddf..22c26066 100644 --- a/theories/Tactics/Either.v +++ b/theories/Tactics/Either.v @@ -18,7 +18,10 @@ From Ltac2 Require Import Ltac2. +Require Import Util.Constr. Require Import Util.Goals. +Require Import Util.Hypothesis. +Require Import Util.MessagesToUser. Require Import Waterprove. (* Switch order of decidable goal. *) @@ -31,7 +34,7 @@ Proof. Qed. (** - Split the proof by case distinction. + Split the proof by case distinction when the goal is a prop Arguments: - [t1 : constr], the first case. @@ -40,12 +43,43 @@ Qed. Does: - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper *) -Ltac2 either_or (t1:constr) (t2:constr) := - let h_id := Fresh.in_goal @h in +Ltac2 either_or_prop (t1:constr) (t2:constr) := + let h_id := Fresh.in_goal @_temp in + let attempt () := + assert ($t1 \/ $t2) as $h_id; + Control.focus 1 1 (fun () => + let automation () := waterprove 4 false Decidability in + first + [ + automation () + | apply or_comm; automation () + ] + ) in + match Control.case attempt with + | Val _ => + let h_val := Control.hyp h_id in + destruct $h_val; + Control.focus 1 1 (fun () => apply (Case.unwrap $t1)); + Control.focus 2 2 (fun () => apply (Case.unwrap $t2)) + | Err exn => throw (Message.of_string "Could not find a proof that the first or the second statement holds.") + end. + +(** + Split the proof by case distinction when the goal is not a prop + + Arguments: + - [t1 : constr], the first case. + - [t2 : constr], the second case. + + Does: + - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper +*) +Ltac2 either_or_type (t1:constr) (t2:constr) := + let h_id := Fresh.in_goal @_temp in let attempt () := assert ({$t1} + {$t2}) as $h_id; Control.focus 1 1 (fun () => - let automation () := waterprove 3 false [] Decidability in + let automation () := waterprove 3 false Decidability in first [ automation () @@ -58,9 +92,32 @@ Ltac2 either_or (t1:constr) (t2:constr) := destruct $h_val; Control.focus 1 1 (fun () => apply (Case.unwrap $t1)); Control.focus 2 2 (fun () => apply (Case.unwrap $t2)) - | Err exn => Control.zero (CaseError "Could not find a proof that the first or the second statement holds.") + | Err exn => throw (Message.of_string "Could not find a proof that the first or the second statement holds.") end. +(** + Split the proof by case distinction. + + Arguments: + - [t1 : constr], the first case. + - [t2 : constr], the second case. + + Does: + - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper +*) +Ltac2 either_or (t1:constr) (t2:constr) := + let goal_is_prop := + lazy_match! goal with + | [ |- ?u] => + (* Check whether [u] is not a proposition. *) + let sort_u := get_value_of_hyp(u) in + check_constr_equal sort_u constr:(Prop) + end + in + if goal_is_prop + then either_or_prop t1 t2 + else either_or_type t1 t2. + Ltac2 Notation "Either" t1(constr) "or" t2(constr) := panic_if_goal_wrapped (); either_or t1 t2. @@ -129,9 +186,45 @@ Proof. exact (left a). Qed. +(** + Split the proof by case distinction if the goal is a prop. + + Arguments: + - [t1 : constr], the first case. + - [t2 : constr], the second case. + - [t3 : constr], the third case. + + Does: + - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper +*) +Ltac2 either_or_or_prop (t1:constr) (t2:constr) (t3:constr) := + let h1_id := Fresh.in_goal @_temp in + let attempt () := + assert ($t1 \/ $t2 \/ $t3) as $h1_id; + Control.focus 1 1 (fun () => + let automation () := waterprove 4 false Decidability in + let g := Control.goal () in first + [ + automation () + ] + ) + in + match Control.case attempt with + | Val _ => + let h2_id := Fresh.in_goal @_temp in + let h_val := Control.hyp h1_id in + destruct $h_val as [_ | $h2_id]; + Control.focus 1 1 (fun () => apply (Case.unwrap $t1)); + Control.focus 2 2 (fun () => + let h2_val := Control.hyp h2_id in + destruct $h2_val; + Control.focus 1 1 (fun () => apply (Case.unwrap $t2)); + Control.focus 2 2 (fun () => apply (Case.unwrap $t3))) + | Err exn => throw (Message.of_string "Could not find a proof that the first, the second or the third statement holds.") + end. (** - Split the proof by case distinction. + Split the proof by case distinction if the goal is not a prop. Arguments: - [t1 : constr], the first case. @@ -141,12 +234,12 @@ Qed. Does: - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper *) -Ltac2 either_or_or (t1:constr) (t2:constr) (t3:constr) := - let h_id := Fresh.in_goal @h in +Ltac2 either_or_or_type (t1:constr) (t2:constr) (t3:constr) := + let h_id := Fresh.in_goal @_temp in let attempt () := assert (sumtriad $t1 $t2 $t3) as $h_id; Control.focus 1 1 (fun () => - let automation () := waterprove 3 false [] Decidability in + let automation () := waterprove 3 false Decidability in let g := Control.goal () in first [ apply double_sumbool_sumtriad_abc; automation () @@ -165,7 +258,28 @@ Ltac2 either_or_or (t1:constr) (t2:constr) (t3:constr) := Control.focus 1 1 (fun () => apply (Case.unwrap $t1)); Control.focus 2 2 (fun () => apply (Case.unwrap $t2)); Control.focus 3 3 (fun () => apply (Case.unwrap $t3)) - | Err exn => Control.zero (CaseError "Could not find a proof that the first, the second or the third statement holds.") + | Err exn => throw (Message.of_string "Could not find a proof that the first, the second or the third statement holds.") + end. + +(** + Split the proof by case distinction. + + Arguments: + - [t1 : constr], the first case. + - [t2 : constr], the second case. + - [t3 : constr], the third case. + + Does: + - splits the proof by case distinction; wraps the resulting goals in the Case.Wrapper +*) +Ltac2 either_or_or (t1:constr) (t2:constr) (t3:constr) := + lazy_match! goal with + | [ |- ?u] => + (* Check whether [u] is not a proposition. *) + let sort_u := get_value_of_hyp(u) in + if (check_constr_equal sort_u constr:(Prop)) + then either_or_or_prop t1 t2 t3 + else either_or_or_type t1 t2 t3 end. Ltac2 Notation "Either" t1(constr) "," t2(constr) "or" t3(constr) := diff --git a/theories/Tactics/Help.v b/theories/Tactics/Help.v index 351dda7c..44627cf8 100644 --- a/theories/Tactics/Help.v +++ b/theories/Tactics/Help.v @@ -18,85 +18,55 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Constr. Require Import Util.Goals. Require Import Util.Hypothesis. +Require Import Util.MessagesToUser. -Ltac2 Type exn ::= [ GoalHintError(string) ]. +Require Import Waterprove. + +Ltac2 Type exn ::= [ Inner ]. Local Ltac2 create_forall_message (v_type: constr) := - Message.concat - (Message.concat - (of_string "The goal is to show a ‘for all’-statement (∀). -Introduce an arbitrary variable of type ") - (Message.of_constr v_type) - ) - ( of_string ". -Use ‘Take ... : (...).’." - ). + concat_list [of_string "The goal is to show a ‘for all’-statement (∀). +Introduce an arbitrary variable of type "; of_constr v_type; of_string ". +Use ‘Take ... : (...).’."]. Local Ltac2 create_implication_message (premise: constr) := - Message.concat - (Message.concat - (of_string "The goal is to show an implication (⇒). -Assume the premise ") - (Message.of_constr premise) - ) - (of_string ". -Use ‘Assume that (...).’." - ). + concat_list [of_string "The goal is to show an implication (⇒). +Assume the premise "; of_constr premise; of_string ". +Use ‘Assume that (...).’."]. Local Ltac2 create_function_message (premise: constr) := - Message.concat - (Message.concat - (of_string "The goal is to construct a map (⇒). -Introduce an arbitrary variable of type ") - (Message.of_constr premise) - ) - ( of_string ". -Use ‘Take ... : (...).’." - ). + concat_list [of_string "The goal is to construct a map (⇒). +Introduce an arbitrary variable of type "; of_constr premise; of_string ". +Use ‘Take ... : (...).’."]. Local Ltac2 create_exists_message (premise: constr) := - Message.concat - (Message.concat - (of_string "The goal is to show a ‘there exists’-statement (∃). -Choose a specific variable of type ") - (Message.of_constr premise) - ) - ( of_string ". -Use ‘Choose ... := (...).’ or ‘Choose (...).’." - ). + concat_list [of_string "The goal is to show a ‘there exists’-statement (∃). +Choose a specific variable of type "; of_constr premise; of_string ". +Use ‘Choose ... := (...).’ or ‘Choose (...).’."]. Local Ltac2 create_goal_wrapped_message () := of_string "Follow the advice in the goal window.". Local Ltac2 create_not_message (negated_type : constr) := - Message.concat - (Message.concat - (Message.concat - (of_string "The goal is to show a negation (¬). -Assume that the negated expression ") - (Message.of_constr negated_type) - ) - (of_string " holds, then show a contradiction.") - ) - (of_string " -Use ‘Assume that (...).’ to do the first step." - ). + concat_list [of_string "The goal is to show a negation (¬). +Assume that the negated expression "; of_constr negated_type; +of_string " holds, then show a contradiction. +Use ‘Assume that (...).’ to do the first step."]. (** - Auxilliary tactic that checks if goal can be shown with minimal automation, i.e. only with core database. + Auxilliary tactic that checks if goal can be shown with automation *) Local Ltac2 solvable_by_core_auto () := - let assertion_id := Fresh.in_goal @assert_goal in + let temp_id := Fresh.in_goal @temp in let goal := Control.goal () in - assert $goal as $assertion_id; - Control.focus 1 1 (fun () => - let hint_databases := Some ((@core)::[]) in - Std.auto Std.Off (Some 1) [] hint_databases - ); - clear $assertion_id. + assert $goal as $temp_id; + Control.focus 1 1 (fun () => waterprove 5 true Main); + clear $temp_id. (** Give a hint indicating a potential step to proving a given proposition [g]. @@ -130,16 +100,14 @@ Show one of the statements, use ‘It suffices to show that (...).’ with the d | Case.Wrapper _ _ => create_goal_wrapped_message () | NaturalInduction.Base.Wrapper _ => create_goal_wrapped_message () | NaturalInduction.Step.Wrapper _ => create_goal_wrapped_message () - | ExpandDef.Goal.Wrapper _ => create_goal_wrapped_message () - | ExpandDef.Hyp.Wrapper _ _ _ => create_goal_wrapped_message () | StateGoal.Wrapper _ => create_goal_wrapped_message () | ByContradiction.Wrapper _ _ => create_goal_wrapped_message () | not ?g => create_not_message g | False => create_goal_wrapped_message () | _ => - match Control.case solvable_by_core_auto with + match Control.case (solvable_by_core_auto) with | Val _ => of_string "The goal can be shown immediately, use ‘We conclude that (...).’." - | Err exn => Control.zero (GoalHintError "No hint available for this goal.") + | Err exn => Control.zero Inner end end. @@ -168,4 +136,4 @@ Ltac2 print_goal_hint (g: constr option) := (** * Help tactic Tries to give a hint how to proceed proving the current goal. *) -Ltac2 Notation "Help" := print_goal_hint None. +Ltac2 Notation "Help" := print_goal_hint None. \ No newline at end of file diff --git a/theories/Tactics/Induction.v b/theories/Tactics/Induction.v index c9b11ec5..cdc85149 100644 --- a/theories/Tactics/Induction.v +++ b/theories/Tactics/Induction.v @@ -17,12 +17,11 @@ (******************************************************************************) Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. Require Import Util.Goals. Require Import Util.Hypothesis. - -Ltac2 Type exn ::= [ NaturalInductionError(string) ]. -Ltac2 raise_natind_error (s:string) := Control.zero (NaturalInductionError s). +Require Import Util.MessagesToUser. (* Lemma to write Sn in goal induction step as n+1. *) Lemma Sn_eq_nplus1 : forall n, S n = n + 1. @@ -35,16 +34,19 @@ Proof. reflexivity. Qed. -(** * induction_with_hypothesis_naming +(** * induction_without_hypothesis_naming Performs mathematical induction. Arguments: - [x: ident], the variable to perform the induction on. Does: - - performs induction on [x]. If [x] is a natural number, the first goal is wrapped in + - performs induction on [x]. + - If [x] is a natural number, the first goal is wrapped in NaturalInduction.Base.Wrapper and the second goal is wrapped in NaturalInduction.Step.Wrapper. + - Otherwise, the resulting cases are wrapped in the StateGoal.Wrapper. + *) Ltac2 induction_without_hypothesis_naming (x: ident) := match Control.case (fun () => Control.hyp x) with @@ -54,13 +56,14 @@ Ltac2 induction_without_hypothesis_naming (x: ident) := let x_hyp := Control.hyp x in let type_x := (get_value_of_hyp x_hyp) in match (Constr.equal type_x constr:(nat)) with - | true => let ih_x := Fresh.in_goal @IH in + | true => let ih_x := Fresh.in_goal @_IH in induction $x_hyp as [ | $x $ih_x]; Control.focus 1 1 (fun () => apply (NaturalInduction.Base.unwrap)); Control.focus 2 2 (fun () => revert $ih_x; rewrite (Sn_eq_nplus1 $x_hyp); apply (NaturalInduction.Step.unwrap)) - | false => induction $x_hyp + | false => induction $x_hyp; Control.enter (fun () => apply StateGoal.unwrap) end. + Ltac2 Notation "We" "use" "induction" "on" x(ident) := panic_if_goal_wrapped (); induction_without_hypothesis_naming x. @@ -74,8 +77,8 @@ Ltac2 Notation "We" "use" "induction" "on" x(ident) := Does: - removes the NaturalInduction.Base.Wrapper from the goal - Raises Exceptions: - - [NaturalInductionError], if the [goal] is the type [t] wrapped in the base case wrapper, + Raises fatal exceptions: + - If the [goal] is the type [t] wrapped in the base case wrapper, i.e. the goal is not of the form [NaturalInduction.Base.Wrapper t]. *) Ltac2 base_case (t:constr) := @@ -83,12 +86,12 @@ Ltac2 base_case (t:constr) := | [|- NaturalInduction.Base.Wrapper ?v] => match Constr.equal v t with | true => apply (NaturalInduction.Base.wrap) - | false => raise_natind_error("Wrong goal specified.") + | false => throw (of_string "Wrong goal specified.") end - | [|- _] => raise_natind_error("No need to indicate showing a base case.") + | [|- _] => throw (of_string "No need to indicate showing a base case.") end. -Ltac2 Notation "We" "first" "show" "the" "base" "case," "namely" that(opt("that")) t(constr) := base_case t. +Ltac2 Notation "We" "first" "show" "the" "base" "case" t(constr) := base_case t. (** * Removes the NaturalInduction.Step.Wrapper. @@ -98,14 +101,14 @@ Ltac2 Notation "We" "first" "show" "the" "base" "case," "namely" that(opt("that" Does: - removes the NaturalInduction.Step.Wrapper from the goal - Raises Exceptions: - - [NaturalInductionError], if the [goal] is not wrapped in the induction step case wrapper, + Raises fatal exceptions: + - If the [goal] is not wrapped in the induction step case wrapper, i.e. the goal is not of the form [NaturalInduction.Step.Wrapper G] for some type [G]. *) Ltac2 induction_step () := lazy_match! goal with | [|- NaturalInduction.Step.Wrapper _] => apply (NaturalInduction.Step.wrap) - | [|- _] => raise_natind_error("No need to indicate showing an induction step.") + | [|- _] => throw (of_string "No need to indicate showing an induction step.") end. Ltac2 Notation "We" "now" "show" "the" "induction" "step" := induction_step (). diff --git a/theories/Tactics/ItHolds.v b/theories/Tactics/ItHolds.v index 5ebbaf52..4ce298a4 100644 --- a/theories/Tactics/ItHolds.v +++ b/theories/Tactics/ItHolds.v @@ -17,79 +17,127 @@ (******************************************************************************) Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Constr. Require Import Util.Goals. Require Import Util.Hypothesis. Require Import Util.Init. +Require Import Util.Since. +Require Import Util.MessagesToUser. Require Import Waterprove. -Local Ltac2 idtac () := (). -(** - Introduce a new sublemma and try to prove it immediately, - optionally using a given lemma. +(** Tries to make the assertion [True] with label [label]. + Throws an error if this fails, i.e. if the label is already used + for another one of the hypotheses. + + This check was separated out from the 'assert'-tactics below because the + '[label] is already used error' would otherwise be caught in + the code meant to catch [AutomationFailure] errors. *) - Arguments: - - [id: ident option], optional name for the new sublemma. If the proof succeeds, it will become a hypothesis (bearing [id] as name). - - [conclusion: constr], the actual content of the new sublemma to prove. - - [proving_lemma: constr], optional reference to a lemma used to prove the new sublemma (via [waterprove)]). +Local Ltac2 try_out_label (label : ident) := + match Control.case (fun () => + assert True as $label by exact I) + with + | Err exn => Control.zero exn + | Val _ => clear $label + end. - Raises exception: - - [AutomationFailure], if [waterprove] fails the prove the sublemma. This happens if the sublemma does not hold, but can also happen if it is simply too difficult for [waterprove]. -*) -Ltac2 assert_and_prove_sublemma (id: ident option) (conclusion: constr) (proving_lemma: constr option) := - let by_arg () := - match proving_lemma with - | None => waterprove 5 false [] Main - | Some lemma => rwaterprove 5 false [fun () => lemma] Main [lemma] [] - end in - let proof_attempt () := (* Check whether identifier is given *) - match id with - | None => - let h := Fresh.in_goal @__wp__h in - ltac2_assert_with_by h conclusion by_arg - | Some id => - ltac2_assert_with_by id conclusion by_arg + +(** Attempts to assert that [claim] holds, if succesful [claim] is added to the local + hypotheses. If [label] is specified [claim] is given [label] as its identifier, otherwise an + identifier starting with '_H' is generated. *) +Local Ltac2 wp_assert (claim : constr) (label : ident option) := + let err_msg (g : constr) := concat_list + [of_string "Could not verify that "; of_constr g; of_string "."] in + let id := + match label with + | None => Fresh.in_goal @_H + | Some label => try_out_label label; label + end + in + match Control.case (fun () => + assert $claim as $id by + (waterprove 5 true Main)) + with + | Val _ => () + | Err (FailedToProve g) => throw (err_msg g) + | Err exn => Control.zero exn + end. + + +(** Attempts to assert that [claim] holds, if succesful [claim] is added to the local + hypotheses. If [label] is specified [claim] is given [label] as its identifier, otherwise an + identifier starting with '_H' is generated. + [xtr_lemma] has to be used in the proof that [claim] holds. + *) +Local Ltac2 core_wp_assert_by (claim : constr) (label : ident option) (xtr_lemma : constr) := + let err_msg (g : constr) := concat_list + [of_string "Could not verify that "; of_constr g; of_string "."] in + let id := + match label with + | None => Fresh.in_goal @_H + | Some label => try_out_label label; label end - in match Control.case proof_attempt with - | Val _ => idtac () - | Err exn => Control.zero exn + in + match Control.case (fun () => + assert $claim as $id by + (rwaterprove 5 true Main xtr_lemma)) + with + | Val _ => () + | Err (FailedToProve g) => throw (err_msg g) + | Err exn => Control.zero exn (* includes FailedToUse error *) end. +(** Adaptation of [core_wp_assert_by] that turns the [FailedToUse] errors + which might be thrown into user readable errors. *) +Local Ltac2 wp_assert_by (claim : constr) (label : ident option) (xtr_lemma : constr) := + wrapper_core_by_tactic (core_wp_assert_by claim label) xtr_lemma. + +(** Adaptation of [core_wp_assert_by] that allows user to use mathematical statements themselves + instead of references to them as extra information for the automation system. + Uses the code in [Since.v]. *) +Local Ltac2 wp_assert_since (claim : constr) (label : ident option) (xtr_claim : constr) := + since_framework (core_wp_assert_by claim label) xtr_claim. + + (** - Introduce a new sublemma and try to prove it immediately using a given lemma. + Attempts to assert a claim and proves it automatically using a specified lemma, + this lemma has to be used. Arguments: - - [lemma: constr], reference to a lemma used to prove the new sublemma (via [waterprove)]). - - [id: ident option], optional name for the new sublemma. If the proof succeeds, it will become a hypothesis (bearing [id] as name). - - [conclusion: constr], the actual content of the new sublemma to prove. + - [xtr_lemma: constr], reference to a lemma used to prove the claim (via [rwaterprove]). + - [label: ident option], optional name for the claim. + If the proof succeeds, it will become a hypothesis (bearing [label] as name). + - [claim: constr], the actual content of the claim to prove. Raises exception: - - [AutomationFailure], if [waterprove] fails the prove the sublemma. This happens if the sublemma does not hold, but can also happen if it is simply too difficult for [waterprove]. + - (fatal) if [rwaterprove] fails to prove the claim using the specified lemma. + - [[label] is already used], if there is already another hypothesis with identifier [label]. *) -Ltac2 Notation "By" lemma(constr) "it" "holds" "that" conclusion(constr) id(opt(seq("(", ident, ")"))) := +Ltac2 Notation "By" xtr_lemma(constr) "it" "holds" "that" claim(constr) label(opt(seq("(", ident, ")"))) := panic_if_goal_wrapped (); - assert_and_prove_sublemma id conclusion (Some lemma). - + wp_assert_by claim label xtr_lemma. + +Ltac2 Notation "Since" xtr_claim(constr) "it" "holds" "that" claim(constr) label(opt(seq("(", ident, ")"))) := + panic_if_goal_wrapped (); + wp_assert_since claim label xtr_claim. (** * It holds that ... (...) - Introduce a new sublemma and try to prove it immediately. - Same as [By ... it holds that ... (...)], - but without using a specified lemma. + Attempts to assert a claim and proves it automatically. - Arguments: - - [id: ident option], optional name for the new sublemma. - If the proof succeeds, - it will become a hypotheses (bearing [id] as name). - - [conclusion: constr], the actual content - of the new sublemma to prove. + Arguments: + - [label: ident option], optional name for the claim. + If the proof succeeds, it will become a hypothesis (bearing [label] as name). + - [claim: constr], the actual content of the claim to prove. Raises exception: - - [AutomationFailure], if [waterprove] fails the prove the sublemma. - This happens if the sublemma does not hold, - but can also happen if it is simply too difficult for [waterprove]. + - (fatal) if [rwaterprove] fails to prove the claim using the specified lemma. + - [[label] is already used], if there is already another hypothesis with identifier [label]. *) -Ltac2 Notation "It" "holds" "that" conclusion(constr) id(opt(seq("(", ident, ")"))) := +Ltac2 Notation "It" "holds" "that" claim(constr) label(opt(seq("(", ident, ")"))) := panic_if_goal_wrapped (); - assert_and_prove_sublemma id conclusion None. \ No newline at end of file + wp_assert claim label. \ No newline at end of file diff --git a/theories/Tactics/ItSuffices.v b/theories/Tactics/ItSuffices.v index a0e5da71..e431708a 100644 --- a/theories/Tactics/ItSuffices.v +++ b/theories/Tactics/ItSuffices.v @@ -17,81 +17,66 @@ (******************************************************************************) Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. Require Import Util.Init. +Require Import Util.Goals. +Require Import Util.Since. +Require Import Util.MessagesToUser. Require Import Waterprove. -Ltac2 Type exn ::= [ AutomationFailure(string) ]. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). -Local Ltac2 raise_automation_failure () := - Control.zero (AutomationFailure "Waterproof could not verify that this statement is enough to prove the goal."). - -(** - Execute a function [f] (assuming it contains an expression that applies the [enough ... by ...] tactic). +(** Attempts to prove that proposed goal is enough to show current goal. + If succesful, replaces current goal by proposed goal. *) +Local Ltac2 wp_enough (new_goal : constr) := + let err_msg := concat_list + [of_string "Could not verify that it suffices to show "; of_constr new_goal; of_string "."] in + match Control.case (fun () => + enough $new_goal by (waterprove 5 true Main)) + with + | Val _ => () + | Err (FailedToProve _) => throw err_msg + | Err exn => Control.zero exn + end. - If it succeeds, print that [statement] is sufficient to show the goal. Raise an error if [f] also does. - Arguments: - - [f: unit -> unit], expression applying the tactic [enough ... by ...]. - - [statement: constr], statement that was 'enough' evidence to proof the goal. Only used for printing purposes. - - Raises exceptions: - - [AutomationFailure], in case [f] throws an error. - This typically happens if the [enough ... by ...]-expression fails to prove the goal. -*) -Local Ltac2 try_enough_expression (f: unit -> unit) (statement: constr) := - match Control.case f with - | Val _ => () - | Err exn => raise_automation_failure () +(** Attempts to prove that proposed goal is enough to show current goal, + given an additional lemma that has to be used in said proof. + If succesful, replaces current goal by proposed goal. *) +Local Ltac2 core_wp_enough_by (new_goal : constr) (xtr_lemma : constr) := + let err_msg := concat_list + [of_string "Could not verify that it suffices to show "; of_constr new_goal; of_string "."] in + match Control.case (fun () => + enough $new_goal by + (rwaterprove 5 true Main xtr_lemma)) + with + | Val _ => () + | Err (FailedToProve _) => throw err_msg + | Err exn => Control.zero exn (* includes FailedToUse error *) end. -(** - Try if the [waterprove] automation would be able to solve the current goal, if [statement] were to hold. - - If it succeeds, the goal is removed, and proving [statement] is added as a new goal. If it fails, an error will be raised. - - Arguments: - - [statement: constr], statement to assume to hold (and to be proven later). - - [proving_lemma: constr], lemma that can help in the proof - - Raises exceptions: - - [AutomationFailure], in case [waterprove] fails to prove the goal, even if [statement] is given. -*) -Ltac2 apply_enough_with_waterprove (statement:constr) (proving_lemma: constr option) := - let hyp_name := Fresh.in_goal @h in - let f () := enough ($hyp_name : $statement) by ( - match proving_lemma with - | None => waterprove 5 true [] Main - | Some lemma => rwaterprove 5 true [fun () => lemma] Main [lemma] [] - end - ) in - try_enough_expression f statement. +(** Adaptation of [core_wp_enough_by] that turns the [FailedToUse] errors + which might be thrown into user readable errors. *) +Local Ltac2 wp_enough_by (claim : constr) (xtr_lemma : constr) := + wrapper_core_by_tactic (core_wp_enough_by claim) xtr_lemma. -(** - Same as the function [apply_enough_with_waterprove]. +(** Adaptation of [core_wp_assert_by] that allows user to use mathematical statements themselves + instead of references to them as extra information for the automation system. + Uses the code in [Since.v]. *) +Local Ltac2 wp_enough_since (claim : constr) (xtr_claim : constr) := + since_framework (core_wp_enough_by claim) xtr_claim. - Take [statement] as a given fact, and try to prove the current goal automatically with it. - - If it succeeds, the goal is removed, and proving [statement] is added as a new goal. If it fails, an error will be raised. +Ltac2 Notation "It" "suffices" "to" "show" that(opt("that")) statement(constr) := + panic_if_goal_wrapped (); + wp_enough statement. - Arguments: - - [statement: constr], statement to assume to hold (and to be proven later). - - Raises exceptions: - - [AutomationFailure], in case no proof is found for the goal, even if [statement] is given. -*) -Ltac2 Notation "It" "suffices" "to" "show" "that" statement(constr) := - apply_enough_with_waterprove statement None. -(** - Same as "It suffices to show that" except it adds an additional lemma temporarily to the underlying automation. +Ltac2 Notation "By" xtr_lemma(constr) "it" "suffices" "to" "show" that(opt("that")) statement(constr) := + panic_if_goal_wrapped (); + wp_enough_by statement xtr_lemma. - Arguments: - - [statement: constr], statement to assume to hold (and to be proven later). - - [lemma: constr], lemma that can help in the proof - - Raises exceptions: - - [AutomationFailure], in case no proof is found for the goal, even if [statement] is given. -*) -Ltac2 Notation "By" lemma(constr) "it" "suffices" "to" "show" "that" statement(constr) := - apply_enough_with_waterprove statement (Some lemma). \ No newline at end of file +Ltac2 Notation "Since" xtr_claim(constr) "it" "suffices" "to" "show" that(opt("that")) statement(constr) := + panic_if_goal_wrapped (); + wp_enough_since statement xtr_claim. \ No newline at end of file diff --git a/theories/Tactics/Obtain.v b/theories/Tactics/Obtain.v new file mode 100644 index 00000000..34d814c6 --- /dev/null +++ b/theories/Tactics/Obtain.v @@ -0,0 +1,139 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). + +Require Import Util.Init. +Local Ltac2 get_type (x: constr) : constr := eval unfold type_of in (type_of $x). + +Require Import Util.Goals. +Require Import Util.MessagesToUser. + +(** Tries to make the assertion [True] with label [label]. + Throws an error if this fails, i.e. if the label is already used + for another one of the hypotheses. + + This check was separated out from the 'assert'-tactics below because the + '[label] is already used error' would otherwise be caught in + the code meant to catch [AutomationFailure] errors. *) + +Local Ltac2 try_out_label (label : ident) := + match Control.case (fun () => + assert True as $label by exact I) + with + | Err exn => Control.zero exn + | Val _ => clear $label + end. + +(** Destructs the statement with label [og_label] into the variable named [var_label] + and the corresponding property. Copies statement [og_label] such that the statement + is preserved despite its destruction. + + The original, not the copy, is destructed because with sigma types the goal + or other hypotheses can depend on the specific instance of the original.*) + +Local Ltac2 copy_and_destruct (og_label : ident) (var_label : ident) := + let og_term := Control.hyp og_label in + let og_type := get_type og_term in + (* make temporary copy; destruct original; make new copy with original label. *) + (* make temporary copy statement *) + let temp_id := Fresh.in_goal @_temp_copy in + assert $og_type as $temp_id; + Control.focus 1 1 (fun () => exact $og_term); + (* (above line) separate from 'assert' + as otherwise $copy_id could not be found..?*) + (* destruct original *) + let prop_label := Fresh.in_goal @_H in + destruct $og_term as [$var_label $prop_label]; + (* make new copy with original label *) + let temp_copy := Control.hyp temp_id in + assert $og_type as $og_label; + Control.focus 1 1 (fun () => exact $temp_copy); + (* remove temporary copy *) + clear $temp_id. + + +(** * + Attempts to obtain a variable from the previous statement. + + Arguments: + - [var : ident], the name of the variable to be obtained. + Does: + - Destructs the previous statement into the variable [var] and its corresponding property. + - Copies the previous statement into a new statement with the same identifier + to preserve the statement despite its destruction. + + Raises fatal exceptions: + - If no statement to destruct or if the previous statement was not an + existence statement or a sigma type. +*) + +Ltac2 obtain_according_to_last (var : ident) := + lazy_match! goal with + | [id_h : _ |- _ ] => + let h := Control.hyp id_h in + let type_h := get_type h in + lazy_match! type_h with + | ex ?pred => copy_and_destruct id_h var + | sig ?pred => copy_and_destruct id_h var + | _ => throw (of_string + "Previous statement is not of the form 'there exists ...'.") + end + | [ |- _] => throw (of_string + "No statement to obtain variable from.") + end. + +(** * + Attempts to obtain a variable from a user-specified statement. + + Arguments: + - [var : ident], the name of the variable to be obtained. + - [hyp : ident], label of the statement the variable should be obtained from. + Does: + - Destructs the statement [hyp] into the variable [var] and its corresponding property. + - Copies the statement [hyp] into a new statement with the same label [hyp] + to preserve the statement despite its destruction. + + Raises exceptions: + - [ObtainError], if no statement to destruct or if the previous statement was not an + existence statement or a sigma type. +*) + +Ltac2 obtain_according_to (var : ident) (hyp : ident) := + let h := Control.hyp hyp in + let type_h := get_type h in + lazy_match! type_h with + | ex ?pred => copy_and_destruct hyp var + | sig ?pred => copy_and_destruct hyp var + | _ => throw (concat_list + [of_string "Statement "; of_constr h; of_string " is not of the form 'there exists ...'."]) + end. + + +Ltac2 Notation "Obtain" "such" a(opt("a")) an(opt("an")) var(ident) := + panic_if_goal_wrapped (); + try_out_label var; + obtain_according_to_last var. + +Ltac2 Notation "Obtain" var(ident) "according" "to" hyp(seq("(", ident, ")")):= + panic_if_goal_wrapped (); + try_out_label var; + obtain_according_to var hyp. diff --git a/theories/Tactics/Take.v b/theories/Tactics/Take.v index 0c7c8aba..895dbba0 100644 --- a/theories/Tactics/Take.v +++ b/theories/Tactics/Take.v @@ -18,20 +18,21 @@ Require Import Ltac2.Ltac2. Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Constr. Require Import Util.Goals. Require Import Util.Hypothesis. - -Ltac2 Type exn ::= [ TakeError(message) ]. +Require Import Util.MessagesToUser. Local Ltac2 too_many_of_type_message (t : constr) := - concat (concat (of_string "Tried to introduce too many variables of type ") (of_constr t)) (of_string "."). + concat_list [of_string "Tried to introduce too many variables of type "; + of_constr t; of_string "."]. Local Ltac2 expected_of_type_instead_of_message (e : constr) (t : constr) := - concat (concat - (concat (of_string "Expected variable of type ") (of_constr e)) - (concat (of_string " instead of ") (of_constr t))) (of_string "."). + concat_list [of_string "Expected variable of type "; of_constr e; + of_string " instead of "; of_constr t; of_string "."]. (** @@ -42,8 +43,8 @@ Local Ltac2 expected_of_type_instead_of_message (e : constr) (t : constr) := - [type: constr]: type of the variable [id]. Does: - Introduces the variable [id]. - Raises Exceptions: - - [TakeError], if the current goal does not require the introduction + Raises fatal exceptions: + - If the current goal does not require the introduction of a variable of type [type], including coercions of [type]. *) Local Ltac2 intro_ident (id : ident) (type : constr) := @@ -53,9 +54,9 @@ Local Ltac2 intro_ident (id : ident) (type : constr) := (* Check whether we need a variable of type [type], including coercions of [type]. *) match check_constr_equal u ct with | true => intro $id - | false => Control.zero (TakeError (too_many_of_type_message type)) + | false => throw (too_many_of_type_message type) end - | [ |- _] => Control.zero (TakeError (too_many_of_type_message type)) + | [ |- _] => throw (too_many_of_type_message type) end. @@ -68,8 +69,8 @@ Local Ltac2 intro_ident (id : ident) (type : constr) := Does: - Introduces the variables in [pair]. - Raises Exceptions: - - [TakeError], if the current goal does not require the introduction of a variable of type [type], including coercions of [type]. + Raises fatal exceptions: + - If the current goal does not require the introduction of a variable of type [type], including coercions of [type]. *) Local Ltac2 intro_per_type (pair : ident list * constr) := let (ids, type) := pair in @@ -83,11 +84,11 @@ Local Ltac2 intro_per_type (pair : ident list * constr) := let ct := get_coerced_type type in match check_constr_equal u ct with | true => List.iter (fun id => intro_ident id type) ids - | false => Control.zero (TakeError (expected_of_type_instead_of_message u type)) + | false => throw (expected_of_type_instead_of_message u type) end - | true => Control.zero (TakeError (of_string "Tried to introduce too many variables.")) + | true => throw (of_string "Tried to introduce too many variables.") end - | [ |- _ ] => Control.zero (TakeError (of_string "Tried to introduce too many variables.")) + | [ |- _ ] => throw (of_string "Tried to introduce too many variables.") end. @@ -101,9 +102,9 @@ Local Ltac2 take (x : (ident list * constr) list) := let sort_u := get_value_of_hyp u in match check_constr_equal sort_u constr:(Prop) with | false => List.iter intro_per_type x - | true => Control.zero (TakeError (of_string "`Take ...` cannot be used to prove an implication (⇨). Use `Assume that ...` instead.")) + | true => throw (of_string "`Take ...` cannot be used to prove an implication (⇨). Use `Assume that ...` instead.") end - | [ |- _ ] => Control.zero (TakeError (of_string "`Take ...` can only be used to prove a `for all`-statement (∀) or to construct a map (→).")) + | [ |- _ ] => throw (of_string "`Take ...` can only be used to prove a `for all`-statement (∀) or to construct a map (→).") end. Ltac2 Notation "Take" x(list1(seq(list1(ident, ","), ":", constr), "and")) := diff --git a/theories/Tactics/ToShow.v b/theories/Tactics/ToShow.v index 17230a76..b5251558 100644 --- a/theories/Tactics/ToShow.v +++ b/theories/Tactics/ToShow.v @@ -20,11 +20,11 @@ Require Import Ltac2.Ltac2. Require Import Util.Constr. Require Import Util.Goals. +Require Import Util.MessagesToUser. Require Import Tactics.Unfold. +Require Import Waterprove. -Ltac2 Type exn ::= [ GoalCheckError(string) ]. - -Local Ltac2 idtac () := (). +Require Import Ltac2.Message. (** Check if the type of the goal is syntactically equal to [t]. @@ -35,43 +35,18 @@ Local Ltac2 idtac () := (). Does: - Prints a confirmation that the goal equals the provided type. - Raises Exceptions: - - [GoalCheckError], if the goal is not syntactically equal to [t]. + Raises fatal exceptions: + - If the goal is not syntactically equal to [t]. *) -Local Ltac2 check_goal := fun (t:constr) => +Local Ltac2 check_goal (t:constr) := lazy_match! goal with | [ |- ?g] => match check_constr_equal g t with - | true => idtac () - (* print (concat (of_string "The goal is indeed: ") (of_constr t))*) - | false => Control.zero (GoalCheckError "Wrong goal specified.") + | true => () + | false => throw (of_string "Wrong goal specified.") end - | [|-_] => Control.zero (GoalCheckError "Wrong goal specified.") end. -(** - Check if the type of the goal is convertible to [t], if so, it replaces the goal by t. - - Arguments: - - [t: constr], any constr to be compared against the goal. - - Does: - - Prints a confirmation that the goal equals the provided type. - - Raises Exceptions: - - [GoalCheckError], if the goal is not convertible to [t]. -*) -Local Ltac2 check_and_change_goal (t:constr) := - lazy_match! goal with - | [ |- ?g] => - match check_constr_equal g t with - | true => - idtac (); - change $t - | false => Control.zero (GoalCheckError "Wrong goal specified.") - end - | [|-_] => Control.zero (GoalCheckError "Wrong goal specified.") - end. (** @@ -83,45 +58,40 @@ Local Ltac2 check_and_change_goal (t:constr) := Does: - Removes the wrapper if the argument matches the wrapped goal, i.e. the goal is of the form [StateGoal.Wrapper t]. - Raises Exceptions: - - [GoalCheckError], if the argument [t] does not match the wrapped goal. + Raises fatal exceptions: + - If the argument [t] does not match the wrapped goal. *) Local Ltac2 unwrap_state_goal (t : constr) := lazy_match! goal with | [|- StateGoal.Wrapper ?g] => match (check_constr_equal g t) with | true => apply StateGoal.wrap - | false => Control.zero (GoalCheckError "Wrong goal specified.") + | false => throw (of_string "Wrong goal specified.") end - | [|- _] => idtac () end. (** - 1) If the goal is wrapped in [ExpandDef.Goal.Wrapper], attempt to remove the wrapper. - - 2) Else if the goal is wrapped in [State.Goal.Wrapper], attempt to remove it. + 1) If the goal is wrapped in [State.Goal.Wrapper], attempt to remove it. - 3) Else, check if the type of the goal is convertible to [t], if so, it replaces the goal by t. + 2) Else, check if the type of the goal is convertible to [t], if so, it replaces the goal by t. Arguments: - [t: constr] - 1,2) type matching the wrapped goal. - 3) any constr to be compared against the goal. + 1) type matching the wrapped goal. + 2) any constr to be compared against the goal. Does: - - 1,2) Removes the wrapper if the argument matches the wrapped goal, i.e. the goal is of the form [ExandDef.Goal.Wrapper t] ([StateGoal.Wrapper t] resp.). - - 3) Prints a confirmation that the goal equals the provided type. + - 1) Removes the wrapper if the argument matches the wrapped goal, i.e. the goal is of the form [StateGoal.Wrapper t]. + - 2) Prints a confirmation that the goal equals the provided type. - Raises Exceptions: - - 1) [ExpandDefError], if the argument [t] does not match the wrapped goal. - - 2) [GoalCheckError], if the argument [t] does not match the wrapped goal. - - 3) [GoalCheckError], if the goal is not convertible to [t]. + Raises fatal exceptions: + - 1) If the argument [t] does not match the wrapped goal. + - 2) If the goal is not convertible to [t]. *) -Local Ltac2 unwrap_or_check_and_change_goal (t : constr) := +Local Ltac2 to_show (t : constr) := lazy_match! goal with - | [|- ExpandDef.Goal.Wrapper _] => goal_as t; change $t (*[goal_as] is from unfold.v*) | [|- StateGoal.Wrapper _] => unwrap_state_goal t; change $t - | [|- _] => panic_if_goal_wrapped (); check_goal t; change $t + | [|- _] => panic_if_goal_wrapped (); check_goal t; change $t end. (* @@ -138,6 +108,6 @@ Local Ltac2 unwrap_or_check_and_change_goal (t : constr) := Optional string keywords do need to have a name, even though the parser will not populate this name. (That's why it reads "that(opt('that'))" instead of "opt('that')". *) -Ltac2 Notation "We" "need" "to" "show" that(opt("that")) colon(opt(":")) t(constr) := unwrap_or_check_and_change_goal t. +Ltac2 Notation "We" "need" "to" "show" that(opt("that")) colon(opt(":")) t(constr) := to_show t. -Ltac2 Notation "To" "show" that(opt("that")) colon(opt(":")) t(constr) := unwrap_or_check_and_change_goal t. +Ltac2 Notation "To" "show" that(opt("that")) colon(opt(":")) t(constr) := to_show t. \ No newline at end of file diff --git a/theories/Tactics/Unfold.v b/theories/Tactics/Unfold.v index e5b0060f..4677b3be 100644 --- a/theories/Tactics/Unfold.v +++ b/theories/Tactics/Unfold.v @@ -17,109 +17,144 @@ (******************************************************************************) Require Import Ltac2.Ltac2. +Require Import Ltac2.Std. +Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). Require Import Util.Goals. +Require Import Util.MessagesToUser. -(** * Tactics that wrap the goal such that the user needs to specify the effect of unfolding in the proof script. *) - -Ltac2 Type exn ::= [ ExpandDefError(string) ]. - -Ltac2 ap_goal_unwrap () := apply ExpandDef.Goal.unwrap. - -Ltac2 ident_to_clause (h : ident) := - {Std.on_hyps := Some [(h, Std.AllOccurrences, Std.InHyp)]; - Std.on_concl := Std.NoOccurrences}. - -Ltac2 ap_hyp_unwrap (h : constr) := apply (fun G => ExpandDef.Hyp.unwrap G _ $h). - +Ltac2 Type exn ::= [ Inner ]. (** - Rewrite a function by its definition, in the goal or in a *single* hypothesis. + Attemtps to unfold definition(s) in a statement according to specified method. + If succesful it also throws a fatal error suggesting the user to replace this + command by an alternative, suitable tactic with the unfolded statement. + E.g. if the statement corresponds with the proof goal, + the user is suggested to use + 'We need to show that ([statement with unfolded definiton])'. Arguments: - - [targets: constr] or [targets: constr list], the target function to unfold, or the target functions (separated by [,]) to unfold. - - [cl: constr], optional suffix with syntax [... in h] for some [h], where [h] is the hypothesis to rewrite the function [f] in. - If omitted, [f] is rewritten in the goal. - - Raises exceptions: - - Panics if the identifier [h] in the suffix [... in h] is not an hypothesis. + - [unfold method: constr -> constr], method to be used for unfolding + unfolding is deemed to be succesful if [unfold_method statement] =\= [statement] + - [def_name: string], optional string used for error message when unfolding + is unsuccesful + - [statement : constr], term in which definitions are to be unfolded. + + Raises fatal exceptions: + - always *) -Ltac2 Notation "Expand" "the" "definition" "of" targets(list1(seq(reference, occurrences), ",")) cl(opt(seq("in", "(", ident, ")"))) := - panic_if_goal_wrapped (); - match cl with - | None => - Std.unfold targets (Notations.default_on_concl None); - ap_goal_unwrap () - | Some cl => - let h_constr := Control.hyp cl in - Std.unfold targets (ident_to_clause cl); - ap_hyp_unwrap h_constr - end. - - -Ltac2 expand_def_framework (unfold_goal : unit -> unit) (unfold_hyp : ident -> unit) (cl : ident option):= - panic_if_goal_wrapped (); - match cl with - | None => - unfold_goal (); - ap_goal_unwrap () - | Some cl => - let h_constr := Control.hyp cl in (* throws error if ident not found in hypotheses *) - unfold_hyp cl; - ap_hyp_unwrap h_constr +Ltac2 unfold_in_statement (unfold_method: constr -> constr) + (def_name : string option) (statement : constr) := + let unfolded_statement := unfold_method statement in + match Constr.equal statement unfolded_statement with + | false => + match! goal with + | [ |- ?g] => + match Constr.equal g statement with + | false => Control.zero Inner + | true => + let msg (unfolded : constr) := concat_list + [of_string "replace line with: + We need to show that "; of_constr unfolded; of_string "."] in + throw (msg unfolded_statement) + end + | [_ : ?hyp |- _ ] => + match Constr.equal hyp statement with + | false => Control.zero Inner + | true => + let msg (unfolded : constr) := concat_list + [of_string "replace line with: + It holds that "; of_constr unfolded; of_string "."] in + throw (msg unfolded_statement) + end + | [ |- _ ] => + let msg (unfolded : constr) := concat_list + [of_string "result: + "; of_constr unfolded; of_string " +Remove this line to continue."] in + throw (msg unfolded_statement) + end + | true => + match def_name with + | None => throw (concat_list + [of_string "definition does not appear in "; of_constr statement; of_string "."]) + | Some def_name => throw (concat_list + [of_string "'"; of_string def_name; of_string "'"; + of_string " does not appear in "; of_constr statement; of_string "."]) + end end. - (** - Removes the [ExpandDef.Goal.Wrapper] from the goal. + Attemtps to unfold definition(s) in a statement according to specified method. + If succesful it prints a message suggesting the user to + use a suitable tactic with the unfolded statement. + E.g. if the statement corresponds with the proof goal, + the user is suggested to use + 'We need to show that ([statement with unfolded definiton])'. Arguments: - - [t: constr], the rewritten goal. - - Raises exceptions: - - [ExpandDefError], if [t] is not syntactically the same as the goal [G] in the wrapper [ExpandDef.Goal.Wrapper G]. - - [ExpandDefError], if the current goal is not wrapped in the [ExpandDef.Goal.Wrapper]. + - [unfold method: constr -> constr], method to be used for unfolding + unfolding is deemed to be succesful if [unfold_method statement] =\= [statement] + - [def_name: string], optional string used for error message when unfolding + is unsuccesful + - [statement : constr], term in which definitions are to be unfolded. + + Raises fatal exceptions: + - none + *) -Ltac2 goal_as (t:constr) := - lazy_match! goal with - | [|- ExpandDef.Goal.Wrapper ?v] => - match Constr.equal v t with - | true => apply (ExpandDef.Goal.wrap) - | false => Control.zero (ExpandDefError "Wrong goal specified.") +Ltac2 unfold_in_statement_no_error (unfold_method: constr -> constr) + (def_name : string option) (statement : constr) := + let unfolded_statement := unfold_method statement in + match Constr.equal statement unfolded_statement with + | false => + match! goal with + | [ |- ?g] => + match Constr.equal g statement with + | false => Control.zero Inner + | true => + let msg (unfolded : constr) := concat_list + [of_string "use: + We need to show that "; of_constr unfolded; of_string "."] in + print (msg unfolded_statement) + end + | [_ : ?hyp |- _ ] => + match Constr.equal hyp statement with + | false => Control.zero Inner + | true => + let msg (unfolded : constr) := concat_list + [of_string "use: + It holds that "; of_constr unfolded; of_string "."] in + print (msg unfolded_statement) end - | [|- ExpandDef.Hyp.Wrapper _ _ _] => Control.zero (ExpandDefError "Specify the effect of expanding definition in *hypothesis*.") - | [|- _] => Control.zero (ExpandDefError "No need to specify the effect of expanding definition.") + | [ |- _ ] => + let msg (unfolded : constr) := concat_list + [of_string "result: + "; of_constr unfolded] in + print (msg unfolded_statement) + end + | true => + match def_name with + | None => print (concat_list + [of_string "definition does not appear in "; of_constr statement; of_string "."]) + | Some def_name => print (concat_list + [of_string "'"; of_string def_name; of_string "'"; + of_string " does not appear in "; of_constr statement; of_string "."]) + end end. -Ltac2 Notation "That" "is," "write" "the" "goal" "as" t(constr) := goal_as t. - - -(** - Removes the [ExpandDef.Hyp.Wrapper] from the goal. - Arguments: - - [h : ident], the hypothesis that has been rewritten. - - [t: constr], the type the hypotheis has been rewritten as. +(* Tactic notation for unfolding generic Gallinea terms, not notations. + For an example of how to used [unfold_in_statement] to unfold notations, + see [tests/tactics/Unfold.v] *) - Raises exceptions: - - [ExpandDefError], if the wrapped goal is not of the form [ExpandDef.Hyp.Wrapper _ t h], that is, h or t has been specified incorrectly. - - [ExpandDefError], if the current goal is not wrapped in the [ExpandDef.Hyp.Wrapper]. -*) -Ltac2 hyp_as (h : ident) (t:constr) := - let h_hyp := Control.hyp h in - lazy_match! goal with - | [|- ExpandDef.Hyp.Wrapper _ ?s ?g] => - match Constr.equal s t with - | true => - match Constr.equal g h_hyp with - | true => apply (fun G => ExpandDef.Hyp.wrap G $s $g) - | false => Control.zero (ExpandDefError "Wrong hypothesis specified.") - end - | false => Control.zero (ExpandDefError "Wrong rewriting specified.") - end - | [|- ExpandDef.Goal.Wrapper _] => Control.zero (ExpandDefError "Specify the effect of expanding definition in *goal*.") - | [|- _] => Control.zero (ExpandDefError "No need to specify the effect of expanding definition.") - end. +Ltac2 Notation "Expand" "the" "definition" "of" targets(list1(seq(reference, occurrences), ",")) "in" statement(constr) := + panic_if_goal_wrapped (); + unfold_in_statement (eval_unfold targets) None statement. -Ltac2 Notation "That" "is," "write" "(" h(ident) ")" "as" t(constr) := hyp_as h t. +Ltac2 Notation "_internal_" "Expand" "the" "definition" "of" targets(list1(seq(reference, occurrences), ",")) "in" statement(constr) := + panic_if_goal_wrapped (); + unfold_in_statement_no_error (eval_unfold targets) None statement. diff --git a/theories/Util/Goals.v b/theories/Util/Goals.v index 888aec2e..2664c3e5 100644 --- a/theories/Util/Goals.v +++ b/theories/Util/Goals.v @@ -17,8 +17,10 @@ (******************************************************************************) Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. Require Import Util.Constr. +Require Import Util.MessagesToUser. Module Case. @@ -61,11 +63,11 @@ Module NaturalInduction. End NaturalInduction. -Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'We' 'first' 'show' 'the' 'base' 'case,' 'namely' ( G )." := +Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'We' 'first' 'show' 'the' 'base' 'case' ( G )." := (NaturalInduction.Base.Wrapper G) ( at level 99, only printing, - format "'[ ' Add the following line to the proof: ']' '//' We first show the base case, namely ( G )." + format "'[ ' Add the following line to the proof: ']' '//' We first show the base case ( G )." ). Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'We' 'now' 'show' 'the' 'induction' 'step.'" := @@ -75,43 +77,6 @@ Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'We' 'now' 'show' ' format "'[ ' Add the following line to the proof: ']' '//' We now show the induction step." ). -Module ExpandDef. - - Module Goal. - - Private Inductive Wrapper (G : Type) : Type := - | wrap : G -> Wrapper G. - - Definition unwrap (G : Type) : Wrapper G -> G := - fun x => match x with wrap _ y => y end. - - End Goal. - - Module Hyp. - - Private Inductive Wrapper (G H : Type) (h : H) : Type := - | wrap : G -> Wrapper G H h. - - Definition unwrap (G H : Type) (h : H) : Wrapper G H h -> G := - fun x => match x with wrap _ _ _ y => y end. - - End Hyp. - -End ExpandDef. - -Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'That' 'is,' 'write' 'the' 'goal' 'as' '(' G ').'" := - (ExpandDef.Goal.Wrapper G) ( - at level 99, - only printing, - format "'[ ' Add the following line to the proof: ']' '//' That is, write the goal as ( G )." - ). - -Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'That' 'is,' 'write' '(' h ')' 'as' '(' H ').'" := - (ExpandDef.Hyp.Wrapper _ H h) ( - at level 99, - only printing, - format "'[ ' Add the following line to the proof: ']' '//' That is, write ( h ) as ( H )." - ). Module StateGoal. @@ -147,14 +112,29 @@ Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'Assume' 'that' '(' format "'[ ' Add the following line to the proof: ']' '//' Assume that ( A )." ). -Ltac2 Type exn ::= [ GoalWrappedError(string) ]. -Ltac2 raise_goal_wrapped_error () := - Control.zero ( - GoalWrappedError "You cannot do this right now, follow the advice in the goal window." +Module StrongIndIndxSeq. + + Private Inductive Wrapper (G : Type) : Type := + | wrap : G -> Wrapper G. + + Definition unwrap (G : Type) : Wrapper G -> G := + fun x => match x with wrap _ y => y end. + +End StrongIndIndxSeq. + +Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'Take' 'k' ':' 'ℕ' 'and' 'assume' 'n(0),...,n(k)' 'are' 'defined.'" := + (StrongIndIndxSeq.Wrapper _) ( + at level 99, + only printing, + format "'[ ' Add the following line to the proof: ']' '//' Take k : ℕ and assume n(0),...,n(k) are defined." ). +Ltac2 raise_goal_wrapped_error () := + throw (of_string "You cannot do this right now, follow the advice in the goal window."). + + (** Throws an error if the goal is wrapped in one of the wrappers above. @@ -165,14 +145,11 @@ Ltac2 panic_if_goal_wrapped () := | [|- Case.Wrapper _ _] => raise_goal_wrapped_error () | [|- NaturalInduction.Base.Wrapper _] => raise_goal_wrapped_error () | [|- NaturalInduction.Step.Wrapper _] => raise_goal_wrapped_error () - | [|- ExpandDef.Goal.Wrapper _] => raise_goal_wrapped_error () - | [|- ExpandDef.Hyp.Wrapper _ _ _] => raise_goal_wrapped_error () | [|- StateGoal.Wrapper _] => raise_goal_wrapped_error () | [|- ByContradiction.Wrapper _ _] => raise_goal_wrapped_error () | [|- _] => () end. - Ltac2 Type exn ::= [ CaseError(string) | InputError(message) ]. (** Removes the Case.Wrapper. @@ -191,9 +168,9 @@ Ltac2 case (t:constr) := | [|- Case.Wrapper ?v _] => match check_constr_equal v t with | true => apply (Case.wrap $v) - | false => Control.zero (CaseError "Wrong case specified.") + | false => throw (of_string "Wrong case specified.") end - | [|- _] => Control.zero (CaseError "No need to specify case.") + | [|- _] => throw (of_string "No need to specify case.") end. Ltac2 Notation "Case" t(constr) := case t. \ No newline at end of file diff --git a/theories/Util/MessagesToUser.v b/theories/Util/MessagesToUser.v new file mode 100644 index 00000000..80f8bea5 --- /dev/null +++ b/theories/Util/MessagesToUser.v @@ -0,0 +1,26 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Export Ltac2.Ltac2. +Require Import Ltac2.Bool. +Require Import Ltac2.Init. + +Require Import Waterproof.Waterproof. + +Ltac2 @ external warn: message -> unit := "coq-core.plugins.coq-waterproof" "warn_external". +Ltac2 @ external throw: message -> unit := "coq-core.plugins.coq-waterproof" "throw_external". diff --git a/theories/Util/Since.v b/theories/Util/Since.v new file mode 100644 index 00000000..48cd768f --- /dev/null +++ b/theories/Util/Since.v @@ -0,0 +1,119 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat ls (of_string ""). + +Require Import Util.Constr. +Require Import Util.MessagesToUser. +Require Import Waterprove. + +Require Import Util.Init. +Local Ltac2 get_type (x: constr) : constr := eval unfold type_of in (type_of $x). + +Local Ltac2 check_if_not_reference (x : constr) := + let type_x := get_type x in + match check_constr_equal type_x constr:(Prop) with + | true => () + | false => + match check_constr_equal type_x constr:(Set) with + | true => () + | false => + match check_constr_equal type_x constr:(Type) with + | true => () + | false => throw (concat_list + [of_string "Cannot use reference "; of_constr x; of_string " with `Since`. +Try `By "; of_constr x; of_string " ...` instead."]) + end + end + end. + +Local Ltac2 check_if_not_statement (x : constr) := + let err_msg := concat_list + [of_string "Cannot use statement "; of_constr x; of_string " with `By`. +Try `Since "; of_constr x; of_string " ...` instead."] + in + let type_x := get_type x in + match check_constr_equal type_x constr:(Prop) with + | true => throw err_msg + | false => + match check_constr_equal type_x constr:(Set) with + | true => throw err_msg + | false => + match check_constr_equal type_x constr:(Type) with + | true => throw err_msg + | false => () + end + end + end. + + +(** Attempts to prove [claimed_cause] with minimal automation, + and then executes [by_tactic] with the proof of [claimed_cause] + as an argument. + Expects that [by_tactic] can throw a [(Waterprove.)FailedToUse] error, + those errors are caught; the extra lemma that the automation failed to use + is used in a new error that is thrown. +*) + +Ltac2 since_framework (by_tactic : constr -> unit) (claimed_cause : constr) := + (* first, check if [claimed_cause] is a statement. *) + check_if_not_reference claimed_cause; + + let id_cause := Fresh.in_goal @_temp in + (* attempt to prove [claimed_cause]*) + match Control.case (fun () => + assert $claimed_cause as $id_cause by + (waterprove 1 true Shorten)) + with + | Err (FailedToProve _) => + throw (concat_list + [of_string "State that "; of_constr claimed_cause; of_string " holds"; + of_string " before using it in `Since ...`."]) + | Err exn => Control.zero exn + | Val _ => + (* use proof of [claimed_cause] in [by_tactic] *) + let cause := Control.hyp id_cause in + match Control.case (fun () => + by_tactic cause) + with + | Val _ => clear $id_cause + | Err (FailedToUse h) => + let type_h := (get_type h) in + clear $id_cause; + throw (concat_list + [of_string "Could not verify this follows from "; of_constr type_h; of_string "."]) + | Err exn => clear $id_cause; Control.zero exn + end + end. + +(** Wrapper for the main functionality of 'By ...'-tactics, + such that the main fucntionality can be reused in 'Since ...'-tactics. + + Checks whether [xtr_lemma] is not a statement (i.e. not a Prop/Set/Type) and + catches [(Waterprove.)FailedToUse] errors to turn them into user-readable errors. *) +Ltac2 wrapper_core_by_tactic (by_tactic : constr -> unit) (xtr_lemma : constr) := + check_if_not_statement xtr_lemma; + match Control.case (fun () => by_tactic xtr_lemma) with + | Val _ => () + | Err (FailedToUse h) => throw (concat_list + [of_string "Could not verify this follows from "; of_constr h; of_string "."]) + | Err exn => Control.zero exn + end. diff --git a/theories/Version.v b/theories/Version.v new file mode 100644 index 00000000..0e859c79 --- /dev/null +++ b/theories/Version.v @@ -0,0 +1,21 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Waterproof.Waterproof. + +Waterproof Print Version. diff --git a/theories/Waterprove.v b/theories/Waterprove.v index 1c2b41e4..a56b6550 100644 --- a/theories/Waterprove.v +++ b/theories/Waterprove.v @@ -16,13 +16,19 @@ (* *) (******************************************************************************) -Require Export Ltac2.Ltac2. +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. +Ltac2 Type exn ::= [ Inner + | FailedToUse (constr) + | FailedToProve (constr) ]. Require Import Waterproof. Require Import Ltac2.Bool. Require Import Ltac2.Init. +Require Import Chains.Inequalities. + Local Ltac2 Type database_type_ffi. Local Ltac2 @ external database_type_main: unit -> database_type_ffi := "coq-core.plugins.coq-waterproof" "database_type_main". @@ -41,18 +47,191 @@ Local Ltac2 database_type_to_ffi (db_type: database_type): database_type_ffi := | Shorten => database_type_shorten () end. -Ltac2 contains_shilded_pattern (): bool := +Local Ltac2 contains_shielded_pattern (): bool := lazy_match! goal with | [ |- forall _, _ ] => true | [ |- exists _, _ ] => true | [ |- _ /\ _] => true | [ |- _ \/ _] => true | [ |- _] => false - end -. + end. + +(** Internal versions of [waterprove] and [rwaterprove]. *) +Local Ltac2 _waterprove (depth: int) (shield: bool) (lems: (unit -> constr) list) (db_type: database_type): unit := + waterprove_ffi depth (shield && contains_shielded_pattern ()) lems (database_type_to_ffi db_type). + +Local Ltac2 _risky_rwaterprove (depth: int) (shield: bool) (lems: (unit -> constr) list) (db_type: database_type) (must : constr list) (forbidden : constr list) : unit := + rwaterprove_ffi depth (shield && contains_shielded_pattern ()) lems (database_type_to_ffi db_type) must forbidden. + + +(** Checks whether [x] is in the current list of hypotheses *) +(* TODO: there could be a better way with [Control.hyps], but this seems to work. *) +Local Ltac2 in_hypotheses (x : constr) := + match! goal with + | [ h_id : _ |- _ ] => + (* check if xtr_lemma matches h *) + let h := Control.hyp h_id in + match Constr.equal x h with + | false => Control.zero Inner + | true => true + end + | [ |- _ ] => false + end. -Ltac2 waterprove (depth: int) (shield: bool) (lems: (unit -> constr) list) (db_type: database_type): unit := - waterprove_ffi depth (shield && contains_shilded_pattern ()) lems (database_type_to_ffi db_type). +Local Ltac2 _rwaterprove (depth: int) (shield: bool) (db_type: database_type) + (xtr_lemma : constr) : unit := + (* workaround for anomalies and troubles with proof finding + when additional extra lemma is one of the hypotheses *) + (* Anonalies occur when + 1. the goal can be solved and [xtr_lemma] is the most recent (and second to most recent?) + hypothesis added + 2. the goal cannot be solved and [xtr_lemma] is a hypothesis *) + match in_hypotheses xtr_lemma with + | false => + (* xtr_lemma is not one of the hypotheses, so we can use rwaterprove without risking anomalies. *) + _risky_rwaterprove depth shield [fun () => xtr_lemma] db_type [xtr_lemma] [] + | true => + _waterprove depth shield [] Main + end. + + +(** Subroutine to solve conjunction of statements piece by piece. + Throws [FailedToProve] error with statement that could not be proven. *) +(* Assumes goal is of the form (g1 /\ ... /\ gn). *) +Local Ltac2 rec waterprove_iterate_conj (depth: int) (shield: bool) + (db_type: database_type) (xtr_lemma : constr option) := + (* accepts optional extra lemma since it can be called by restricted version + [rwaterprove_iterate_conj] when extra lemma is no longer required but still available *) + let list_lemmas := + match xtr_lemma with + | None => [] + | Some lem => [fun () => lem] + end + in + lazy_match! goal with + (* recursion step *) + | [ |- ?g1 /\ ?remainder ] => + split; + (* Attempt to prove 1st goal *) + match Control.case (fun () => Control.focus 1 1 (fun () => + _waterprove depth shield list_lemmas db_type)) + with + | Val _ => waterprove_iterate_conj depth shield db_type xtr_lemma + | Err exn => Control.zero (FailedToProve g1) + end + (* base case *) + | [ |- _ ] => + (* Prove remaining goal. *) + match Control.case (fun () => + _waterprove depth shield list_lemmas db_type) + with + | Val _ => () + | Err exn => Control.zero (FailedToProve (Control.goal ())) + end + end. -Ltac2 rwaterprove (depth: int) (shield: bool) (lems: (unit -> constr) list) (db_type: database_type) (must_use: constr list) (forbidden: constr list): unit := - rwaterprove_ffi depth (shield && contains_shilded_pattern ()) lems (database_type_to_ffi db_type) must_use forbidden. \ No newline at end of file +(** Subroutine to solve conjunction of statements piece by piece + using an extra lemma which has to be used. + Throws [FailedToProve] error with statement that could not be proven. + Throws [FailedToUse] error if no proof of the statements used the extra lemma. *) +(* Assumes goal is of the form (g1 /\ ... /\ gn). *) +Local Ltac2 rec rwaterprove_iterate_conj (depth: int) (shield: bool) + (db_type: database_type) (xtr_lemma : constr) := + lazy_match! goal with + (* recursion step *) + | [ |- ?g1 /\ _ ] => + split; + (* Attempt to prove 1st goal with extra lemma. *) + match Control.case (fun () => Control.focus 1 1 (fun () => + _rwaterprove depth shield db_type xtr_lemma)) + with + | Val _ => + (* succesfully used lemma; prove remaining goals, but without restriction. *) + waterprove_iterate_conj depth shield db_type (Some xtr_lemma) + | Err exn => + (* failed, could be due to restriction; try to prove without. *) + match Control.case (fun () => Control.focus 1 1 (fun () => + _waterprove depth shield [] db_type)) + with + | Val _ => + (* succesful. prove remaining statements with restriction. *) + rwaterprove_iterate_conj depth shield db_type xtr_lemma + | Err exn => Control.zero (FailedToProve g1) + end + end + (* base case *) + | [ |- _ ] => + (* Prove remaining goal with restriction. *) + match Control.case (fun () => + _rwaterprove depth shield db_type xtr_lemma) + with + | Val _ => () + | Err exn => (* failed, if due to restricition, give feedback *) + (* check if it would work without lemma *) + match Control.case (fun () => + _waterprove depth shield [] db_type) + with + | Err exn => Control.zero (FailedToProve (Control.goal ())) + | Val _ => (* problem is the extra lemma *) + Control.zero (FailedToUse xtr_lemma) + end + end + end. + + +(** External versions of (restricted) automation. + (In)equality chains are attempted piece by piece. *) + +Lemma _and_assoc1 (A B C : Prop) : A /\ B /\ C -> (A /\ B) /\ C. +Proof. apply and_assoc. Qed. + +(** Attempts to prove current goal. + Throws [FailedToProve] error with statement that could not be proven. + (In)equality chains are attempted piece by piece. +*) +Ltac2 waterprove (depth: int) (shield: bool) (db_type: database_type) := + lazy_match! goal with + (* prove (in)equality chain piece by piece to give better feedback *) + | [ |- total_statement _ ] => + cbn; repeat (apply _and_assoc1); (* get chain into shape: g1 /\ ... /\ gn *) + waterprove_iterate_conj depth shield db_type None + (* regular proof attempt *) + | [ |- _] => + match Control.case (fun () => + _waterprove depth shield [] db_type) + with + | Val _ => () + | Err exn => Control.zero (FailedToProve (Control.goal ())) + end + end. + +(** Attempts to prove current goal. + Throws [FailedToProve] error with statement that could not be proven. + Throws [FailedToUse] error with [xtr_lemma] if no proof could be found + that uses it. + (In)equality chains are attempted piece by piece. +*) +Ltac2 rwaterprove (depth: int) (shield: bool) (db_type: database_type) (xtr_lemma : constr) := + lazy_match! goal with + (* prove (in)equality chain piece by piece to give better feedback *) + | [ |- total_statement _ ] => + cbn; repeat (apply _and_assoc1); (* get chain into shape: g1 /\ ... /\ gn *) + rwaterprove_iterate_conj depth shield db_type xtr_lemma + (* regular proof attempt *) + | [ |- _] => + (* Prove goal with restriction. *) + match Control.case (fun () => + _rwaterprove depth shield db_type xtr_lemma) + with + | Val _ => () + | Err exn => (* failed, if due to restricition, give feedback *) + (* check if it would work without lemma *) + match Control.case (fun () => + _waterprove depth shield [] db_type) + with + | Err exn => Control.zero (FailedToProve (Control.goal ())) + | Val _ => (* problem is the extra lemma *) + Control.zero (FailedToUse xtr_lemma) + end + end + end. \ No newline at end of file diff --git a/theories/dune b/theories/dune index 313d2078..0a536990 100644 --- a/theories/dune +++ b/theories/dune @@ -4,4 +4,13 @@ (flags :standard) (plugins coq-waterproof.plugin coq-core.plugins.ltac coq-core.plugins.ltac2)) -(include_subdirs qualified) \ No newline at end of file +(include_subdirs qualified) + +(rule + (alias runtest) + (deps (alias ../install)) + (action (progn + (echo "Testing:") + (run pwd) + (run python3 ../../../tests/test-folder.py ../../../../waterproof-exercises) +))) From 3ee230f6f753ccdcb841d27a59f11cfd88485e57 Mon Sep 17 00:00:00 2001 From: Rodolphe Lepigre Date: Wed, 24 Jan 2024 09:00:41 +0100 Subject: [PATCH 15/27] Adapt to coq/coq#18327 (projection opacity) (#41) --- src/wp_auto.ml | 9 +++++---- src/wp_auto.mli | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/wp_auto.ml b/src/wp_auto.ml index 2d9cd934..a92d527c 100644 --- a/src/wp_auto.ml +++ b/src/wp_auto.ml @@ -81,9 +81,10 @@ let exact (h: hint): unit tactic = (** Same function as {! Auto.exists_evaluable_reference} *) -let exists_evaluable_reference (env: Environ.env) (evaluable_ref: Tacred.evaluable_global_reference): bool = match evaluable_ref with - | Tacred.EvalConstRef _ -> true - | Tacred.EvalVarRef v -> try ignore(Environ.lookup_named v env); true with Not_found -> false +let exists_evaluable_reference (env: Environ.env) (evaluable_ref: Names.Evaluable.t): bool = match evaluable_ref with + | Evaluable.EvalConstRef _ -> true + | Evaluable.EvalProjectionRef _ -> true + | Evaluable.EvalVarRef v -> try ignore(Environ.lookup_named v env); true with Not_found -> false (* All the definitions below are inspired by the coq-core hidden library (i.e not visible in the API) but modified for Waterproof *) @@ -313,4 +314,4 @@ let wp_auto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (dbna This function acts the same as {! wp_auto} but will fail if all proof found contain at least one must-use lemma that is unused or one hint that is in the [forbidden] list. *) let rwp_auto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (dbnames: hint_db_name list) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): trace tactic = - gen_wp_auto log ~n lems (Some dbnames) must_use_tactics forbidden_tactics \ No newline at end of file + gen_wp_auto log ~n lems (Some dbnames) must_use_tactics forbidden_tactics diff --git a/src/wp_auto.mli b/src/wp_auto.mli index c1062fc2..fd96007f 100644 --- a/src/wp_auto.mli +++ b/src/wp_auto.mli @@ -21,7 +21,7 @@ open Backtracking (** Same function as {! Auto.exists_evaluable_reference} *) -val exists_evaluable_reference : Environ.env -> Tacred.evaluable_global_reference -> bool +val exists_evaluable_reference : Environ.env -> Names.Evaluable.t -> bool (** Prints "idtac" if the [log] field is [true] @@ -87,4 +87,4 @@ val rwp_auto : string list -> Pp.t list -> Pp.t list -> - Backtracking.trace Proofview.tactic \ No newline at end of file + Backtracking.trace Proofview.tactic From 623df9be51d899a2bff9f598770a20504a718d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Sun, 28 Jan 2024 11:36:06 +0100 Subject: [PATCH 16/27] Adapt to coq/coq#18529 (no Dyn.anonymous) (#47) --- src/exceptions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exceptions.ml b/src/exceptions.ml index 7b8b541d..1d372498 100644 --- a/src/exceptions.ml +++ b/src/exceptions.ml @@ -21,7 +21,7 @@ open Pp (** Basic exception info *) -let fatal_flag: 'a Exninfo.t = Exninfo.make () +let fatal_flag: unit Exninfo.t = Exninfo.make "waterproof_fatal_flag" (** Type of exceptions used in Wateproof From 48b4d59c8e86889af374a1188179aa2dabf20d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Fri, 9 Feb 2024 16:16:07 +0100 Subject: [PATCH 17/27] Adapt to coq/coq#18624 (Tac2ffi / Tac2val split) (#49) --- src/g_waterproof.mlg | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_waterproof.mlg b/src/g_waterproof.mlg index ede7e46e..2bec1bc1 100644 --- a/src/g_waterproof.mlg +++ b/src/g_waterproof.mlg @@ -28,6 +28,7 @@ open Proofview open Proofview.Notations open Tac2expr open Tac2ffi +open Ltac2_plugin.Tac2val open Exceptions open Hint_dataset_declarations From eb307d8faa80194b1e6485329836f27effb3dc6a Mon Sep 17 00:00:00 2001 From: Rodolphe Lepigre Date: Wed, 3 Apr 2024 13:11:11 +0200 Subject: [PATCH 18/27] Adapt to coq/coq#18546. (#48) --- src/wp_auto.ml | 2 +- src/wp_auto.mli | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp_auto.ml b/src/wp_auto.ml index a92d527c..1c072320 100644 --- a/src/wp_auto.ml +++ b/src/wp_auto.ml @@ -81,7 +81,7 @@ let exact (h: hint): unit tactic = (** Same function as {! Auto.exists_evaluable_reference} *) -let exists_evaluable_reference (env: Environ.env) (evaluable_ref: Names.Evaluable.t): bool = match evaluable_ref with +let exists_evaluable_reference (env: Environ.env) (evaluable_ref: Evaluable.t): bool = match evaluable_ref with | Evaluable.EvalConstRef _ -> true | Evaluable.EvalProjectionRef _ -> true | Evaluable.EvalVarRef v -> try ignore(Environ.lookup_named v env); true with Not_found -> false diff --git a/src/wp_auto.mli b/src/wp_auto.mli index fd96007f..e3b11f01 100644 --- a/src/wp_auto.mli +++ b/src/wp_auto.mli @@ -21,7 +21,7 @@ open Backtracking (** Same function as {! Auto.exists_evaluable_reference} *) -val exists_evaluable_reference : Environ.env -> Names.Evaluable.t -> bool +val exists_evaluable_reference : Environ.env -> Evaluable.t -> bool (** Prints "idtac" if the [log] field is [true] From e349d404ab42d2bd884b6ac8457450d099bc9838 Mon Sep 17 00:00:00 2001 From: Pierre Roux Date: Mon, 22 Apr 2024 10:47:40 +0200 Subject: [PATCH 19/27] Adapt to https://github.com/coq/coq/pull/18880 (#52) --- tests/automation/Chains.v | 6 +++--- tests/libs/Analysis/StrongInductionIndexSequence.v | 6 +++--- tests/tactics/Conclusion.v | 6 +++--- tests/tactics/Contradiction.v | 2 +- tests/tactics/ItHolds.v | 12 ++++++------ tests/tactics/ItSuffices.v | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/automation/Chains.v b/tests/automation/Chains.v index dd312ad7..4ac4b4be 100644 --- a/tests/automation/Chains.v +++ b/tests/automation/Chains.v @@ -42,8 +42,8 @@ Proof. Abort. (** Test restricted automation. *) -Variable P : Prop. -Variable h : P -> a = b. +#[local] Parameter P : Prop. +#[local] Parameter h : P -> a = b. #[local] Hint Extern 1 => symmetry : core. (* Test 3: fails without extra lemma. *) @@ -79,4 +79,4 @@ Goal P -> (P -> b = c) -> (& b = b = c = b = b = c = c). Proof. intros H1 H2. Fail rwaterprove 5 true Main constr:(h). -Abort. \ No newline at end of file +Abort. diff --git a/tests/libs/Analysis/StrongInductionIndexSequence.v b/tests/libs/Analysis/StrongInductionIndexSequence.v index c6134c19..41d8e005 100644 --- a/tests/libs/Analysis/StrongInductionIndexSequence.v +++ b/tests/libs/Analysis/StrongInductionIndexSequence.v @@ -19,7 +19,7 @@ Require Import Ltac2.Ltac2. Require Import Waterproof.Libs.Analysis.StrongInductionIndexSequence. -Variable Q : nat -> Prop. +#[local] Parameter Q : nat -> Prop. (* Test 1: without other Waterproof tactics. *) @@ -69,7 +69,7 @@ Inductive Color : Set := | blue : Color | orange : Color. -Variable P : ℕ → Color. +#[local] Parameter P : ℕ → Color. Parameter infinitely_many_blues : ∀ k : ℕ, ∃ m : ℕ, (m ≥ k)%nat ∧ P(m) = blue. Lemma exercise_10_7_6 : @@ -91,4 +91,4 @@ Define the index sequence n inductively. We show both statements. * We conclude that (P(n_kplus1) = blue). * We conclude that (n(k) < n_kplus1)%nat. -Qed. \ No newline at end of file +Qed. diff --git a/tests/tactics/Conclusion.v b/tests/tactics/Conclusion.v index 28701eac..38547873 100644 --- a/tests/tactics/Conclusion.v +++ b/tests/tactics/Conclusion.v @@ -140,8 +140,8 @@ Abort. (** Additional tests 'By ...' clause. *) (* Test 6: unable to show goal without means required for proof. *) -Variable A B : Prop. -Variable f : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter f : A -> B. Goal A -> B. intro H. Fail We conclude that B. @@ -163,7 +163,7 @@ Proof. Qed. (* Test 9: unable to show goal with irrelevant lemma. *) -Variable g : B -> A. +#[local] Parameter g : B -> A. Goal B. Proof. Fail By g we conclude that B. diff --git a/tests/tactics/Contradiction.v b/tests/tactics/Contradiction.v index 92757a2b..a4082fbf 100644 --- a/tests/tactics/Contradiction.v +++ b/tests/tactics/Contradiction.v @@ -61,7 +61,7 @@ Abort. (** Test 4: fails if previous statement is not a contraditcion to some earlier statement. *) -Variable P Q A : Prop. +#[local] Parameter P Q A : Prop. Goal P -> Q. intro H. Fail Contradiction. diff --git a/tests/tactics/ItHolds.v b/tests/tactics/ItHolds.v index ecedeafc..52e3c424 100644 --- a/tests/tactics/ItHolds.v +++ b/tests/tactics/ItHolds.v @@ -138,8 +138,8 @@ Abort. (** Tests for restricted version of 'By ...' clause *) -Variable A B : Prop. -Variable f : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter f : A -> B. (* Test 5: regular check that assertion works. *) Goal A -> False. @@ -195,7 +195,7 @@ Proof. Abort. (* Test 10: 'By ...' fails if additional lemma is not needed for proof assertion. *) -Variable g : B -> A. +#[local] Parameter g : B -> A. Goal A -> False. Proof. intro H. @@ -272,7 +272,7 @@ Proof. Abort. (* Test 18: possible goal with right hypothesis provided. *) -Variable C : Prop. +#[local] Parameter C : Prop. Goal A -> False. Proof. intro H. @@ -286,7 +286,7 @@ Abort. terms in the hypotheses, (see workaround [Wateprove._rwaterprove]). (* Test 19: possible goal with wrong hypothesis provided. *) -Variable D : Prop. +#[local] Parameter D : Prop. Goal A -> False. Proof. intro H. @@ -304,4 +304,4 @@ Goal A -> (A -> B) -> B. Proof. intros Ha Hf. By Ha it holds that B. -Abort. \ No newline at end of file +Abort. diff --git a/tests/tactics/ItSuffices.v b/tests/tactics/ItSuffices.v index 14ee2ea7..21f9f931 100644 --- a/tests/tactics/ItSuffices.v +++ b/tests/tactics/ItSuffices.v @@ -74,8 +74,8 @@ Abort. (* Test 5: unable to show goal is enough if it does not imply current goal *) -Variable A B : Prop. -Variable g : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter g : A -> B. Goal B. Fail It suffices to show that A. Abort. @@ -94,7 +94,7 @@ Proof. Abort. (* Test 8: unable to goal is enough with irrelevant lemma. *) -Variable h : A. +#[local] Parameter h : A. Goal B. Proof. Fail By h it suffices to show that A. @@ -112,4 +112,4 @@ Goal B. Proof. pose g. Since (A -> B) it suffices to show that A. -Abort. \ No newline at end of file +Abort. From 397cb8e07ae15fe41f98b772f08568e278b270d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Gilbert?= Date: Tue, 23 Apr 2024 19:54:11 +0200 Subject: [PATCH 20/27] Adapt to coq/coq#18938 (EConstr.ERelevance) (#53) --- src/wp_eauto.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp_eauto.ml b/src/wp_eauto.ml index 51716f94..f19e3758 100644 --- a/src/wp_eauto.ml +++ b/src/wp_eauto.ml @@ -58,7 +58,7 @@ let e_assumption: trace tactic = Tacticals.tclZEROMSG (str "No applicable tactic.") else let not_ground = occur_existential sigma concl in - let map (decl: ('a, types) Declaration.pt): trace tactic = + let map (decl: (_, types, _) Declaration.pt): trace tactic = let id = Declaration.get_id decl in let t = Declaration.get_type decl in begin From d686be7fefd5f5bb147c999a2e588887203c743a Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Fri, 3 May 2024 13:22:30 +0200 Subject: [PATCH 21/27] Update README.md Update installation instructions --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index e52dac3d..ec474f07 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,8 @@ You can also install coq-waterproof without using opam (though it is greatly rec ```bash $ git clone https://github.com/impermeable/coq-waterproof.git && cd coq-waterproof -$ autoreconf -i -s -$ ./configure $ dune build -p coq-waterproof -$ make && make install # It is needed to compile and install with both `dune` and `make` -$ dune install coq-waterproof +$ dune install -p coq-waterproof ``` ## Usage From 924d35ae278f1829740dd813705c1ecf82c35c47 Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Sat, 4 May 2024 08:24:00 +0200 Subject: [PATCH 22/27] Testmaster (#54) Implement a better way to do a case analyse on a type Co-authored-by: DikieDick --- theories/Util/Since.v | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/theories/Util/Since.v b/theories/Util/Since.v index 48cd768f..97e8dbfc 100644 --- a/theories/Util/Since.v +++ b/theories/Util/Since.v @@ -30,19 +30,13 @@ Local Ltac2 get_type (x: constr) : constr := eval unfold type_of in (type_of $x) Local Ltac2 check_if_not_reference (x : constr) := let type_x := get_type x in - match check_constr_equal type_x constr:(Prop) with - | true => () - | false => - match check_constr_equal type_x constr:(Set) with - | true => () - | false => - match check_constr_equal type_x constr:(Type) with - | true => () - | false => throw (concat_list + match! type_x with + | Prop => () + | Set => () + | Type => () + | _ => throw (concat_list [of_string "Cannot use reference "; of_constr x; of_string " with `Since`. Try `By "; of_constr x; of_string " ...` instead."]) - end - end end. Local Ltac2 check_if_not_statement (x : constr) := @@ -51,17 +45,11 @@ Local Ltac2 check_if_not_statement (x : constr) := Try `Since "; of_constr x; of_string " ...` instead."] in let type_x := get_type x in - match check_constr_equal type_x constr:(Prop) with - | true => throw err_msg - | false => - match check_constr_equal type_x constr:(Set) with - | true => throw err_msg - | false => - match check_constr_equal type_x constr:(Type) with - | true => throw err_msg - | false => () - end - end + match! type_x with + | Prop => throw err_msg + | Set => throw err_msg + | Type => throw err_msg + | _ => () end. From 5fb5ffa9e6971096d7313c4132c09cd0cf885912 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Sat, 4 May 2024 08:43:18 +0200 Subject: [PATCH 23/27] fix: Change 'Variable' to 'local Parameter' --- tests/automation/Chains.v | 6 +++--- tests/libs/Analysis/StrongInductionIndexSequence.v | 6 +++--- tests/tactics/Conclusion.v | 6 +++--- tests/tactics/Contradiction.v | 2 +- tests/tactics/ItHolds.v | 12 ++++++------ tests/tactics/ItSuffices.v | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/automation/Chains.v b/tests/automation/Chains.v index dd312ad7..4ac4b4be 100644 --- a/tests/automation/Chains.v +++ b/tests/automation/Chains.v @@ -42,8 +42,8 @@ Proof. Abort. (** Test restricted automation. *) -Variable P : Prop. -Variable h : P -> a = b. +#[local] Parameter P : Prop. +#[local] Parameter h : P -> a = b. #[local] Hint Extern 1 => symmetry : core. (* Test 3: fails without extra lemma. *) @@ -79,4 +79,4 @@ Goal P -> (P -> b = c) -> (& b = b = c = b = b = c = c). Proof. intros H1 H2. Fail rwaterprove 5 true Main constr:(h). -Abort. \ No newline at end of file +Abort. diff --git a/tests/libs/Analysis/StrongInductionIndexSequence.v b/tests/libs/Analysis/StrongInductionIndexSequence.v index c6134c19..41d8e005 100644 --- a/tests/libs/Analysis/StrongInductionIndexSequence.v +++ b/tests/libs/Analysis/StrongInductionIndexSequence.v @@ -19,7 +19,7 @@ Require Import Ltac2.Ltac2. Require Import Waterproof.Libs.Analysis.StrongInductionIndexSequence. -Variable Q : nat -> Prop. +#[local] Parameter Q : nat -> Prop. (* Test 1: without other Waterproof tactics. *) @@ -69,7 +69,7 @@ Inductive Color : Set := | blue : Color | orange : Color. -Variable P : ℕ → Color. +#[local] Parameter P : ℕ → Color. Parameter infinitely_many_blues : ∀ k : ℕ, ∃ m : ℕ, (m ≥ k)%nat ∧ P(m) = blue. Lemma exercise_10_7_6 : @@ -91,4 +91,4 @@ Define the index sequence n inductively. We show both statements. * We conclude that (P(n_kplus1) = blue). * We conclude that (n(k) < n_kplus1)%nat. -Qed. \ No newline at end of file +Qed. diff --git a/tests/tactics/Conclusion.v b/tests/tactics/Conclusion.v index 28701eac..38547873 100644 --- a/tests/tactics/Conclusion.v +++ b/tests/tactics/Conclusion.v @@ -140,8 +140,8 @@ Abort. (** Additional tests 'By ...' clause. *) (* Test 6: unable to show goal without means required for proof. *) -Variable A B : Prop. -Variable f : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter f : A -> B. Goal A -> B. intro H. Fail We conclude that B. @@ -163,7 +163,7 @@ Proof. Qed. (* Test 9: unable to show goal with irrelevant lemma. *) -Variable g : B -> A. +#[local] Parameter g : B -> A. Goal B. Proof. Fail By g we conclude that B. diff --git a/tests/tactics/Contradiction.v b/tests/tactics/Contradiction.v index 92757a2b..a4082fbf 100644 --- a/tests/tactics/Contradiction.v +++ b/tests/tactics/Contradiction.v @@ -61,7 +61,7 @@ Abort. (** Test 4: fails if previous statement is not a contraditcion to some earlier statement. *) -Variable P Q A : Prop. +#[local] Parameter P Q A : Prop. Goal P -> Q. intro H. Fail Contradiction. diff --git a/tests/tactics/ItHolds.v b/tests/tactics/ItHolds.v index ecedeafc..52e3c424 100644 --- a/tests/tactics/ItHolds.v +++ b/tests/tactics/ItHolds.v @@ -138,8 +138,8 @@ Abort. (** Tests for restricted version of 'By ...' clause *) -Variable A B : Prop. -Variable f : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter f : A -> B. (* Test 5: regular check that assertion works. *) Goal A -> False. @@ -195,7 +195,7 @@ Proof. Abort. (* Test 10: 'By ...' fails if additional lemma is not needed for proof assertion. *) -Variable g : B -> A. +#[local] Parameter g : B -> A. Goal A -> False. Proof. intro H. @@ -272,7 +272,7 @@ Proof. Abort. (* Test 18: possible goal with right hypothesis provided. *) -Variable C : Prop. +#[local] Parameter C : Prop. Goal A -> False. Proof. intro H. @@ -286,7 +286,7 @@ Abort. terms in the hypotheses, (see workaround [Wateprove._rwaterprove]). (* Test 19: possible goal with wrong hypothesis provided. *) -Variable D : Prop. +#[local] Parameter D : Prop. Goal A -> False. Proof. intro H. @@ -304,4 +304,4 @@ Goal A -> (A -> B) -> B. Proof. intros Ha Hf. By Ha it holds that B. -Abort. \ No newline at end of file +Abort. diff --git a/tests/tactics/ItSuffices.v b/tests/tactics/ItSuffices.v index 14ee2ea7..21f9f931 100644 --- a/tests/tactics/ItSuffices.v +++ b/tests/tactics/ItSuffices.v @@ -74,8 +74,8 @@ Abort. (* Test 5: unable to show goal is enough if it does not imply current goal *) -Variable A B : Prop. -Variable g : A -> B. +#[local] Parameter A B : Prop. +#[local] Parameter g : A -> B. Goal B. Fail It suffices to show that A. Abort. @@ -94,7 +94,7 @@ Proof. Abort. (* Test 8: unable to goal is enough with irrelevant lemma. *) -Variable h : A. +#[local] Parameter h : A. Goal B. Proof. Fail By h it suffices to show that A. @@ -112,4 +112,4 @@ Goal B. Proof. pose g. Since (A -> B) it suffices to show that A. -Abort. \ No newline at end of file +Abort. From f752caf8a82dcac315e4d0dafea75700ab1d35b7 Mon Sep 17 00:00:00 2001 From: Jim Portegies Date: Sat, 4 May 2024 09:05:16 +0200 Subject: [PATCH 24/27] feat: add a test for awp_autorewrite from coq-master --- tests/automation/databases/Core.v | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/automation/databases/Core.v b/tests/automation/databases/Core.v index 31544674..d90a95ea 100644 --- a/tests/automation/databases/Core.v +++ b/tests/automation/databases/Core.v @@ -31,4 +31,13 @@ Qed. Goal forall x y: nat, forall f: nat -> nat, x = y -> f (S x) = f (S y). Proof. waterprove 5 false Main. -Qed. \ No newline at end of file +Qed. + +(** + * Test of wp_autorewrite +*) +Goal forall P : nat -> Prop, forall p q : nat, p = q -> P q -> P p. +Proof. + intros. + waterprove 5 false Main. +Qed. From 38796908fb4360350ceaa35f7381b7433142937b Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Sat, 4 May 2024 10:14:58 +0200 Subject: [PATCH 25/27] Refactor: incorporate some changes from 8.17 and update version numbers (#57) --- CHANGES.md | 5 +++++ coq-waterproof.opam | 2 +- dune-project | 2 +- src/META.coq-waterproof | 2 +- src/g_waterproof.mlg | 2 +- theories/Libs/Analysis/Sequences.v | 9 ++++++--- theories/Libs/Analysis/Subsequences.v | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f45f010..a61f448a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Change log for the coq-waterproof library +## Version 2.1.1+8.19 + +- Compatibility with earlier OCaml compilers +- Fixes for the strong induction tactic + ## Version 2.1.0+8.17 - Improve the `Either` tactic: now proves and destructs ordinary 'ors' when the goal is a proposition diff --git a/coq-waterproof.opam b/coq-waterproof.opam index c40b2e0b..8e9419c7 100644 --- a/coq-waterproof.opam +++ b/coq-waterproof.opam @@ -1,6 +1,6 @@ opam-version: "2.0" name: "coq-waterproof" -version: "2.1.0+8.19" +version: "2.1.1+8.19" maintainer: "Jim Portegies " authors: [ "Jelle Wemmenhove" diff --git a/dune-project b/dune-project index 4f464b0a..638ab005 100644 --- a/dune-project +++ b/dune-project @@ -3,6 +3,6 @@ (package (name coq-waterproof) - (version 2.1.0+8.19) + (version 2.1.1+8.19) (synopsis "Coq proofs in a style that resembles non-mechanized mathematical proofs") (depends coq-core coq-stdlib)) diff --git a/src/META.coq-waterproof b/src/META.coq-waterproof index e72c9005..3782be03 100644 --- a/src/META.coq-waterproof +++ b/src/META.coq-waterproof @@ -1,6 +1,6 @@ package "waterproof" ( directory = "." - version = "2.1.0+8.19" + version = "2.1.1+8.19" description = "Waterproof plugin" requires = "coq-core.plugins.ltac coq-core.plugins.ltac2" archive(byte) = "waterproof.cma" diff --git a/src/g_waterproof.mlg b/src/g_waterproof.mlg index 2bec1bc1..4b78530d 100644 --- a/src/g_waterproof.mlg +++ b/src/g_waterproof.mlg @@ -34,7 +34,7 @@ open Exceptions open Hint_dataset_declarations open Waterprove -let waterproof_version : string = "2.1.0+8.19" +let waterproof_version : string = "2.1.1+8.19" } VERNAC COMMAND EXTEND AutomationShieldEnableSideEff CLASSIFIED AS SIDEFF diff --git a/theories/Libs/Analysis/Sequences.v b/theories/Libs/Analysis/Sequences.v index 53c1088b..9c2d9481 100644 --- a/theories/Libs/Analysis/Sequences.v +++ b/theories/Libs/Analysis/Sequences.v @@ -89,9 +89,12 @@ Proof. Either (x <= 0) or (0 < x). - Case (x <= 0). Choose n := 1%nat. - It holds that (INR n > INR 0). - It holds that (x <= 0%nat). - We conclude that (INR n > x). + We claim that (INR 1 > INR 0). + { We need to show that ( 1 > 0 ). + We conclude that (1 > 0). + } + It holds that (x <= INR 0). + We conclude that (n > x). - Case (0 < x). By archimed it holds that (IZR( up x) > x ∧ IZR( up x ) - x ≤ 1). It holds that (IZR( up x ) > x). diff --git a/theories/Libs/Analysis/Subsequences.v b/theories/Libs/Analysis/Subsequences.v index eb07f87b..e245f977 100644 --- a/theories/Libs/Analysis/Subsequences.v +++ b/theories/Libs/Analysis/Subsequences.v @@ -282,7 +282,7 @@ Proof. | 0 => f(0) | S k => Nat.max(f(l), seq_of_max(f, k)) end)(g, n + 1))%nat. - It holds that (n + 1 = S n)%nat (i). + It holds that (n + 1 = S n)%nat. It suffices to show that (g(n) ≤ seq_of_max(g, n))%nat. We conclude that (g(n) ≤ seq_of_max(g, n))%nat. Qed. From 463871ac23786430ba3347a073841caebfbf0ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Jes=C3=BAs=20Gallego=20Arias?= Date: Sun, 5 May 2024 11:01:36 +0200 Subject: [PATCH 26/27] [build] Fix use of plugin aliases in findlib loading. (#58) Note that both lines where loading the same plugin, but activating different syntax rules; that's not really allowed as it leaves the system in a partial state. If something like that is needed, true two plugins are necessary (Which is not hard to support nowadays). --- src/databases.mlg | 4 ++-- theories/Automation/Framework.v | 2 +- theories/Waterproof.v | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/databases.mlg b/src/databases.mlg index 8219e95e..2b94763d 100644 --- a/src/databases.mlg +++ b/src/databases.mlg @@ -16,7 +16,7 @@ (* *) (******************************************************************************) -DECLARE PLUGIN "coq-waterproof.databases" +DECLARE PLUGIN "coq-waterproof.plugin" { @@ -73,4 +73,4 @@ VERNAC COMMAND EXTEND DatabaseQuery CLASSIFIED AS QUERY Feedback.msg_notice ((str "Decidability databases :") ++ List.fold_left (fun acc database_name -> acc ++ str " " ++ str database_name) (str "") (get_current_databases Decidability)); Feedback.msg_notice ((str "Shorten databases :") ++ List.fold_left (fun acc database_name -> acc ++ str " " ++ str database_name) (str "") (get_current_databases Shorten)) } -END \ No newline at end of file +END diff --git a/theories/Automation/Framework.v b/theories/Automation/Framework.v index d7b2c0b6..3846d12f 100644 --- a/theories/Automation/Framework.v +++ b/theories/Automation/Framework.v @@ -17,4 +17,4 @@ (******************************************************************************) From Ltac2 Require Import Init. -Declare ML Module "waterproof:coq-waterproof.databases". \ No newline at end of file +Declare ML Module "coq-waterproof.plugin". diff --git a/theories/Waterproof.v b/theories/Waterproof.v index 644e59ae..3846d12f 100644 --- a/theories/Waterproof.v +++ b/theories/Waterproof.v @@ -17,4 +17,4 @@ (******************************************************************************) From Ltac2 Require Import Init. -Declare ML Module "waterproof:coq-waterproof.plugin". \ No newline at end of file +Declare ML Module "coq-waterproof.plugin". From 37f263c5b8f9bd80963c7fcad4258f557ce2b7ee Mon Sep 17 00:00:00 2001 From: jim-portegies <36723906+jim-portegies@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:35:35 +0200 Subject: [PATCH 27/27] feat: Postponing choices in the Choose tactic (#59) * Merge 8.17 into main (#61) Main change according to (PR #58): we only declare one plugin now. * Renamed variables to prevent having actual exercise solution in repo (#64) * Specialize (#51) * feat: first version of waterproof specialize tactic * feat: Improve new specialize tactic, so it throws an error when a wrong variable is getting introduced. * Avoid doubling hypothesis, allow for multiple binders, prevent matching on functions, rename hypothesis to user-specified one --------- Co-authored-by: Jelle * feat: Postponing choices in the Choose tactic * No longer rename variables in exists-statements --------- Co-authored-by: Jelle --- _CoqProject.in | 2 + .../Analysis/StrongInductionIndexSequence.v | 34 ++++--- tests/tactics/Choose.v | 10 ++ tests/tactics/ItHolds.v | 64 +++++++++++- tests/tactics/Specialize.v | 98 +++++++++++++++++++ theories/Tactics.v | 1 + theories/Tactics/Choose.v | 18 +++- theories/Tactics/ItHolds.v | 49 +++++++++- theories/Tactics/Specialize.v | 74 ++++++++++++++ theories/Util/Goals.v | 18 ++++ 10 files changed, 345 insertions(+), 23 deletions(-) create mode 100644 tests/tactics/Specialize.v create mode 100644 theories/Tactics/Specialize.v diff --git a/_CoqProject.in b/_CoqProject.in index a368a3cd..d3fbefb0 100644 --- a/_CoqProject.in +++ b/_CoqProject.in @@ -67,6 +67,7 @@ theories/Tactics/Help.v theories/Tactics/Induction.v theories/Tactics/ItHolds.v theories/Tactics/ItSuffices.v +theories/Tactics/Specialize.v theories/Tactics/Take.v theories/Tactics/ToShow.v theories/Tactics/Unfold.v @@ -137,6 +138,7 @@ tests/tactics/Either.v tests/tactics/Induction.v tests/tactics/ItHolds.v tests/tactics/ItSuffices.v +tests/tactics/Specialize.v tests/tactics/Take.v tests/tactics/ToShow.v tests/tactics/Unfold.v diff --git a/tests/libs/Analysis/StrongInductionIndexSequence.v b/tests/libs/Analysis/StrongInductionIndexSequence.v index 41d8e005..7d8d4e78 100644 --- a/tests/libs/Analysis/StrongInductionIndexSequence.v +++ b/tests/libs/Analysis/StrongInductionIndexSequence.v @@ -57,38 +57,40 @@ Abort. -(* Test 3: more complicated example from actual exercises, - i.e. with function notation 'n(k)' and 'P(n(k)) = blue' as property. *) +(* Test 3: more complicated example inspired by actual exercises, + i.e. with function notation 'n(k)' and 'candy_seq(n(k)) = sweet' as property. *) Require Import Waterproof.Notations. Require Import Waterproof.Automation. Waterproof Enable Automation RealsAndIntegers. Waterproof Enable Automation Intuition. -Inductive Color : Set := -| blue : Color -| orange : Color. +Inductive Flavor : Set := +| sweet : Flavor +| sour : Flavor +| salty : Flavor. -#[local] Parameter P : ℕ → Color. -Parameter infinitely_many_blues : ∀ k : ℕ, ∃ m : ℕ, (m ≥ k)%nat ∧ P(m) = blue. -Lemma exercise_10_7_6 : - ∃ n : ℕ → ℕ, (is_index_seq n) ∧ (∀ k : ℕ, P (n k) = blue). +#[local] Parameter candy_seq : ℕ → Flavor. +Parameter infinitely_many_sweet : ∀ k : ℕ, ∃ m : ℕ, (m ≥ k)%nat ∧ candy_seq(m) = sweet. + +Goal + ∃ n : ℕ → ℕ, (is_index_seq n) ∧ (∀ k : ℕ, candy_seq (n k) = sweet). Proof. Define the index sequence n inductively. -- By (infinitely_many_blues) it holds that - (∃ m : ℕ, (m ≥ 0)%nat ∧ P(m) = blue). +- By (infinitely_many_sweet) it holds that + (∃ m : ℕ, (m ≥ 0)%nat ∧ candy_seq(m) = sweet). Obtain such an m. Choose n_0 := m. - We conclude that (P(n_0) = blue). + We conclude that (candy_seq(n_0) = sweet). - Take k : ℕ and assume n(0),...,n(k) are defined. - Assume that (∀ l : ℕ, (l ≤ k)%nat ⇒ P(n(l)) = blue). + Assume that (∀ l : ℕ, (l ≤ k)%nat ⇒ candy_seq(n(l)) = sweet). Assume that (∀ l : ℕ, (l < k)%nat ⇒ (n(l) < n(l+1))%nat). - By (infinitely_many_blues) it holds that - (∃ m : ℕ, (m ≥ (n k) + 1)%nat ∧ P(m) = blue). + By (infinitely_many_sweet) it holds that + (∃ m : ℕ, (m ≥ (n k) + 1)%nat ∧ candy_seq(m) = sweet). Obtain such an m. Choose n_kplus1 := m. We show both statements. - * We conclude that (P(n_kplus1) = blue). + * We conclude that (candy_seq(n_kplus1) = sweet). * We conclude that (n(k) < n_kplus1)%nat. Qed. diff --git a/tests/tactics/Choose.v b/tests/tactics/Choose.v index 00cab25f..25b343b6 100644 --- a/tests/tactics/Choose.v +++ b/tests/tactics/Choose.v @@ -59,3 +59,13 @@ Goal forall n : nat, ( ( (n = n) \/ (n + 1 = n + 1) ) -> (n + 1 = n + 1)). intro n. Fail Choose m := n. Abort. + +(** Test 5: Choose a blank *) +Goal exists n : nat, n + 1 = n + 1. + Choose n := (_). +Abort. + +(** Test 6: Choose a named evar *) +Goal exists n : nat, n + 1 = n + 1. + Choose n := (?[m]). +Abort. diff --git a/tests/tactics/ItHolds.v b/tests/tactics/ItHolds.v index 5a24c59d..33abe5ce 100644 --- a/tests/tactics/ItHolds.v +++ b/tests/tactics/ItHolds.v @@ -23,9 +23,9 @@ Require Import Lra. (* Set Default Timeout 1. *) +Require Import Waterproof.Tactics. Require Import Waterproof.Automation. Require Import Waterproof.Notations. -Require Import Waterproof.Tactics. Require Import Waterproof.Util.Assertions. Waterproof Enable Automation RealsAndIntegers. @@ -306,6 +306,8 @@ Proof. By Ha it holds that B. Abort. +(* -------------------------------------------------------------------------- *) + (* Test 21: we can use "It holds that" using boolean statements *) Goal 1 < 2. Proof. @@ -319,3 +321,63 @@ Proof. Since (orb true false) it holds that (1 < 2). assumption. Qed. + +(* -------------------------------------------------------------------------- *) + +(** Test 23: Test whether wrapper for specialize works *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + Use x := 6 in (H1). + It holds that (6 >= 5 -> True) (i). +Abort. + +(** Test 24: Test wrapper specialize blocks other tactics *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + Use x := 6 in (H1). + Fail We claim that (False). +Abort. + +(** Test 25: Test wrapper specialize fails with wrong statement *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + It holds that (3 >= 5 -> True). (* test whether automation could show this*) + Use x := 6 in (H1). + Fail It holds that (3 >= 5 -> True). +Abort. + +(** Test 26: Test succesful renaming hypothesis from specialize + even if another instance is added inbetween. *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + Use x := 6 in (H1). + assert (6 >= 5 -> True) as decoy by exact (H1 6). + It holds that (6 >= 5 -> True) (i). + ltac1:(rename i into ii). (* test for hypohtesis without producing output *) + Fail Check _H. (* '_H' is auto-generated by 'specialize' tactic *) +Abort. + +(** Test 27: Test fail renaming hypothesis from specialize + if user-specified name is already used. *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + Use x := 6 in (H1). + Fail It holds that (6 >= 5 -> True) (H1). +Abort. + + +(* TODO (cf. test below): wrong statement error should have priority over wrong label. *) + +(** Test 28: Test fail renaming hypothesis from specialize + if wrong statement user-specified name is already used. *) +Goal (forall x : nat, x >= 5 -> True) -> True. +Proof. + intro H1. + Use x := 6 in (H1). + Fail It holds that (6 >= 5 -> True) (H1). +Abort. diff --git a/tests/tactics/Specialize.v b/tests/tactics/Specialize.v new file mode 100644 index 00000000..08d26cf7 --- /dev/null +++ b/tests/tactics/Specialize.v @@ -0,0 +1,98 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. + +Require Import Waterproof.Tactics. +Require Import Waterproof.Automation. + +(** Test 0: This should be the expected behavior. *) +Goal (forall n : nat, n = n) -> True. +Proof. +intro H. +Use n := 3 in (H). +It holds that (3 = 3). +Abort. + +(** Test 1: This should fail as the wrong variable name is chosen. *) +Goal (forall n : nat, n = n) -> True. +Proof. +intro H. +Fail Use m := (3) in (H). +Abort. + +(** Test 2: This should fail because the wrong goal is specified. *) +Goal (forall n : nat, n = n) -> True. +Proof. +intro H. +Use n := (3) in (H). +Fail It holds that (True). +Abort. + +(** Test 3: This should fail because first the wrapper needs to be resolved. *) +Goal (forall n : nat, n = n) -> True. +Proof. +intro H. +Use n := (3) in (H). +Fail exact I. +Abort. + +(** It should be possible to use an outside lemma *) +Local Parameter F : nat -> nat. +Local Parameter F_identity : forall n : nat, F n = n. + +Goal True. +Proof. +Fail It holds that (F 3 = 3). +Use n := (5) in (F_identity). +It holds that (F 5 = 5). +Abort. + +(** Test 4: cannot use specialize tactic for function, + throw readable error message stating as much. *) +Definition f : nat -> nat := fun (n : nat) => n. +Goal False. +Proof. + Fail Use n := 5 in (f). +Abort. + +(** Test 5: original universal quantifier hypohtesis left unchanged. *) +Goal (forall n : nat, n = n) -> True. +Proof. +intro H. +Use n := 3 in (H). +ltac1:(rename H into HH). (* test for hypohtesis without producing output *) +Abort. + +(** Test 6: multiple variable specifications *) +Goal (forall n m : nat, n = m) -> False. +Proof. +intro H. +Use n := 3, m := 4 in (H). +It holds that (3 = 4). +Abort. + +(** Test 7: multiple variable specifications, different order *) +Goal (forall n m : nat, n = m) -> False. +Proof. +intro H. +Use m := 4, n := 3 in (H). +Fail It holds that (4 = 3). (* as expected :) *) +It holds that (3 = 4). +Abort. diff --git a/theories/Tactics.v b/theories/Tactics.v index 65703147..3c71b81f 100644 --- a/theories/Tactics.v +++ b/theories/Tactics.v @@ -33,6 +33,7 @@ Require Export Tactics.Help. Require Export Tactics.Induction. Require Export Tactics.ItHolds. Require Export Tactics.ItSuffices. +Require Export Tactics.Specialize. Require Export Tactics.Take. Require Export Tactics.ToShow. Require Export Tactics.Unfold. diff --git a/theories/Tactics/Choose.v b/theories/Tactics/Choose.v index 7fbb0270..9c58859e 100644 --- a/theories/Tactics/Choose.v +++ b/theories/Tactics/Choose.v @@ -22,6 +22,9 @@ Require Import Ltac2.Message. Require Import Util.Goals. Require Import Util.MessagesToUser. +Local Ltac2 concat_list (ls : message list) : message := + List.fold_right concat (of_string "") ls. + (* Ltac2 Type exn ::= [ ChooseError(string) ]. *) @@ -44,9 +47,13 @@ Require Import Util.MessagesToUser. Ltac2 choose_variable_in_exists_goal_with_renaming (s:ident) (t:constr) := lazy_match! goal with | [ |- exists _ : _, _] => - pose ($s := $t); + set ($s := $t); let v := Control.hyp s in let w := Fresh.fresh (Fresh.Free.of_goal ()) @_defeq in + match Constr.has_evar t with + | true => warn (concat_list [of_string "Please come back to this line later to make a definite choice for "; of_ident s; of_string "."]) + | _ => () + end; exists $v; assert ($w : $v = $t) by reflexivity | [ |- _ ] => throw (of_string "`Choose` can only be applied to 'exists' goals.") @@ -68,14 +75,17 @@ Ltac2 choose_variable_in_exists_goal_with_renaming (s:ident) (t:constr) := *) Ltac2 choose_variable_in_exists_no_renaming (t:constr) := lazy_match! goal with - | [ |- exists _ : _, _] => exists $t + | [ |- exists _ : _, _] => + match Constr.has_evar t with + | true => warn (concat_list [of_string "Please come back to this line later to make a definite choice."]); eexists $t + | false => exists $t + end | [ |- _ ] => throw (of_string "`Choose` can only be applied to 'exists' goals.") end. -Ltac2 Notation "Choose" s(opt(seq(ident, ":="))) t(constr) := +Ltac2 Notation "Choose" s(opt(seq(ident, ":="))) t(open_constr) := panic_if_goal_wrapped (); match s with | None => choose_variable_in_exists_no_renaming t | Some s => choose_variable_in_exists_goal_with_renaming s t end. - diff --git a/theories/Tactics/ItHolds.v b/theories/Tactics/ItHolds.v index 8bbe93d6..19425512 100644 --- a/theories/Tactics/ItHolds.v +++ b/theories/Tactics/ItHolds.v @@ -106,6 +106,25 @@ Local Ltac2 wp_assert_by (claim : constr) (label : ident option) (xtr_lemma : co Local Ltac2 wp_assert_since (claim : constr) (label : ident option) (xtr_claim : constr) := since_framework (core_wp_assert_by claim label) xtr_claim. +(** + Removes a [StateHyp.Wrapper] wrapper from the goal. + + Arguments: The statement in the specified hypothesis + + Does: + - Removes the wrapper [StateHyp.Wrapper A G]. + + Raises exception: + - (fatal) if the wrong hypothesis is specified. +*) +Local Ltac2 unwrap_state_hyp (statement : constr) := + lazy_match! goal with + | [|- StateHyp.Wrapper ?s _] => + if check_constr_equal s statement + then apply (StateHyp.wrap $s) + else throw (of_string "Wrong statement specified.") + | [|- _] => () + end. (** Attempts to assert a claim and proves it automatically using a specified lemma, @@ -141,6 +160,32 @@ Ltac2 Notation "Since" xtr_claim(constr) "it" "holds" "that" claim(constr) label - (fatal) if [rwaterprove] fails to prove the claim using the specified lemma. - [[label] is already used], if there is already another hypothesis with identifier [label]. *) + +Local Ltac2 wp_assert_with_unwrap (claim : constr) (label : ident option) := + (* Try out label first. + Code results in wrong error if done inside repeated match.. *) + match label with | None => () | Some label => try_out_label label end; + + match! goal with + | [h : ?s |- StateHyp.Wrapper ?s ?h_spec _] => + let h_constr := Control.hyp h in + (* sanity check "h = h_spec" *) + if check_constr_equal h_constr h_spec + then () + else fail; + + if check_constr_equal s claim + then apply (StateHyp.wrap $s) + else throw (of_string "Wrong statement specified."); + (* rename ident generated in specialize with user-specified label*) + match label with + | None => () + | Some label => Std.rename [(h, label)] + end + | [|- _] => + panic_if_goal_wrapped (); + wp_assert claim label + end. + Ltac2 Notation "It" "holds" "that" claim(constr) label(opt(seq("(", ident, ")"))) := - panic_if_goal_wrapped (); - wp_assert claim label. \ No newline at end of file + wp_assert_with_unwrap claim label. \ No newline at end of file diff --git a/theories/Tactics/Specialize.v b/theories/Tactics/Specialize.v new file mode 100644 index 00000000..61ac281b --- /dev/null +++ b/theories/Tactics/Specialize.v @@ -0,0 +1,74 @@ +(******************************************************************************) +(* This file is part of Waterproof-lib. *) +(* *) +(* Waterproof-lib is free software: you can redistribute it and/or modify *) +(* it under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 3 of the License, or *) +(* (at your option) any later version. *) +(* *) +(* Waterproof-lib is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU General Public License *) +(* along with Waterproof-lib. If not, see . *) +(* *) +(******************************************************************************) + +Require Import Ltac2.Ltac2. +Require Import Ltac2.Message. + +Require Import Util.Init. +Require Import Util.Goals. +Require Import Util.MessagesToUser. + + +(** + Specializes a hypothesis that starts with a for-all statement. + + Arguments: + - [s : ident], name of the variable to choose + - [choice : constr], choice for the variable + - [in_hyp : ident], name of the hypothesis to specialize. + + Raises fatal exceptions: + - If the hypothesis [in_hyp] does not start with a for-all statement. +*) + +Local Ltac2 _ident_to_hyp_list (ls : (ident * constr) list) : (Std.hypothesis * constr) list +:= List.map (fun (i, x) => (Std.NamedHyp i, x)) ls. + +Local Ltac2 wp_specialize (var_choice_list : (ident * constr) list) (h:constr) := + (* let h := Control.hyp in_hyp in *) + (* let named_hyp := Std.NamedHyp s in *) + let statement := eval unfold type_of in (type_of $h) in + (* let specialized_statement := eval unfold type_of in (type_of ($h $choice)) in *) + lazy_match! statement with + | _ -> ?x => (* Exclude matching on functions (naming codomain necessary) *) + throw (of_string "`Pick ... in (*)` only works if (*) starts with a for-all quantifier.") + | forall _ : _, _ => + let w := Fresh.fresh (Fresh.Free.of_goal ()) @_H in + Std.specialize (h, Std.ExplicitBindings (_ident_to_hyp_list var_choice_list)) + (Some (Std.IntroNaming (Std.IntroIdentifier w))) ; + (* specialize $h with ($named_hyp := $choice) as $w; *) + (* Wrap the goal *) + let constr_w := Control.hyp w in + let type_w := Constr.type constr_w in + apply (StateHyp.unwrap $type_w $constr_w) + | _ => throw (of_string "`Pick ... in (*)` only works if (*) starts with a for-all quantifier.") + end. + +(** + Specializes a hypothesis that starts with a for-all statement. + + Arguments: + - [s : ident], name of the variable to choose + - [choice : constr], choice for the variable + - [in_hyp : ident], name of the hypothesis to specialize. + + Raises fatal exceptions: + - If the hypothesis [in_hyp] does not start with a for-all statement. +*) +Ltac2 Notation "Use" s(list1(seq(ident, ":=", constr), ",")) "in" "(" in_hyp(constr) ")" := + wp_specialize s in_hyp. diff --git a/theories/Util/Goals.v b/theories/Util/Goals.v index 26c06969..2822b741 100644 --- a/theories/Util/Goals.v +++ b/theories/Util/Goals.v @@ -96,6 +96,23 @@ Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'We' 'need' 'to' 's format "'[ ' Add the following line to the proof: ']' '//' We need to show that ( G ). '//' or write: '//' We conclude that ( G ). '//' if no intermediary proof steps are required." ). +Module StateHyp. + + Private Inductive Wrapper (A : Type) (h : A) (G : Type) : Type := + | wrap : G -> Wrapper A h G. + + Definition unwrap (A : Type) (h : A) (G : Type) : Wrapper A h G -> G := + fun x => match x with wrap _ _ _ y => y end. + +End StateHyp. + +Notation "'Add' 'the' 'following' 'line' 'to' 'the' 'proof:' 'It' 'holds' 'that' '(' A ').'" := + (StateHyp.Wrapper A _ _) ( + at level 99, + only printing, + format "'[ ' Add the following line to the proof: ']' '//' It holds that ( A )." + ). + Module ByContradiction. Private Inductive Wrapper (A G : Type) : Type := @@ -147,6 +164,7 @@ Ltac2 panic_if_goal_wrapped () := | [|- NaturalInduction.Base.Wrapper _] => raise_goal_wrapped_error () | [|- NaturalInduction.Step.Wrapper _] => raise_goal_wrapped_error () | [|- StateGoal.Wrapper _] => raise_goal_wrapped_error () + | [|- StateHyp.Wrapper _ _ _] => raise_goal_wrapped_error () | [|- ByContradiction.Wrapper _ _] => raise_goal_wrapped_error () | [|- _] => () end.