pax_global_header00006660000000000000000000000064152231614170014514gustar00rootroot0000000000000052 comment=16f8d646bc5e30d685cff6a49da5cf3cd4f9fa88 coqhammer-1.3.3-9.2/000077500000000000000000000000001522316141700140025ustar00rootroot00000000000000coqhammer-1.3.3-9.2/AGENTS.md000066400000000000000000000115141522316141700153070ustar00rootroot00000000000000## Overview CoqHammer is an automated reasoning tool for Rocq (Coq), written mostly in OCaml. It consists of two separately packaged components: 1. **coq-hammer-tactics** — the `sauto` general proof search tactic and friends (`hauto`, `qauto`, `sfirstorder`, ...). Sources: `src/lib/`, `src/tactics/`, `theories/Tactics/`. 2. **coq-hammer** — the `hammer` tool: premise selection by machine learning, translation of goals to FOL, invocation of external ATPs (Vampire, CVC4, Eprover, Z3), and proof reconstruction with `sauto`. Sources: `src/plugin/`, `theories/Plugin/`. Depends on coq-hammer-tactics being **installed**. Each git branch targets one Rocq/Coq version. Never branch from or merge with `master` for release work — `master` tracks unstable Rocq development. ## Build commands Two build systems are maintained in parallel: coq_makefile (primary, via `Makefile`) and dune. ```bash make # build tactics, install them, then build the plugin make tactics # build coq-hammer-tactics only make plugin # build coq-hammer plugin (requires tactics installed) make install # install both make dune # dune build of both packages make dune-install make clean ``` The plugin build cannot proceed without an installed coq-hammer-tactics (see `Makefile.coq.plugin.local`, which links against the `coq-hammer-tactics.lib` findlib package) — hence `make` interleaves `install-tactics` between the two builds. Two small standalone binaries are built alongside the plugin and installed into the Rocq bin directory: `predict` (C++, machine-learning premise selection: kNN, naive Bayes, random forest — `src/predict/`) and `htimeout` (C, `src/htimeout/`). ## Tests Tests are `.v` files compiled with the **installed** plugin (`rocq c` with no `-Q`/`-R` flags), so install before testing. ```bash make tests # full test suite (tests/plugin + tests/tactics) make quicktest # just plugin_test.vo and tactics_test.vo make test-plugin make test-tactics ``` Run a single test file directly: ```bash cd tests/plugin && rocq c bugs.v # or basic.v, arith.v, lists.v, ... cd tests/tactics && rocq c tactics_test.v ``` `tests/plugin/*.v` require external ATPs to be installed since they actually run `hammer`. ## Automation (`just`) The `justfile` wraps the build/release/branch workflow. Run `just` with no arguments to list all recipes. Release conventions and the underlying scripts live in `scripts/` (see `scripts/release-lib.sh` for branch/tag/version naming). Never run release work from `master` — it tracks unstable Rocq (see Overview). ## Architecture The `hammer` pipeline (entry point `src/plugin/hammer_main.ml`, vernacular/tactic syntax in `src/plugin/g_hammer.mlg`): 1. **Premise selection** — `features.ml` extracts features from the goal and accessible lemmas; the external `predict` binary ranks the most relevant premises. 2. **Translation** — Coq terms are converted to an intermediate `hh_term` representation (`hh_term.ml`, `coqterms.ml`, `coq_convert.ml`), then translated to untyped first-order logic (`coq_transl.ml`, driven by options in `coq_transl_opts.ml`) and emitted as TPTP (`tptp_out.ml`). 3. **ATP invocation** — `provers.ml` runs the external provers in parallel (`parallel.ml`, `timeout.ml`) and parses back the list of premises used in the found proof. 4. **Reconstruction** — the goal is re-proved inside Coq from the returned premises using `sauto`-based tactics (`src/tactics/tacbest.ml` searches over tactic/option combinations). `sauto` itself (`src/tactics/sauto.ml`, ~1400 lines, the core of the tactics package) is a general proof search procedure for CIC, heavily configurable via `s_opts` (`sauto.mli`); option parsing from tactic syntax lives in `tacopts.ml`, and the tactic grammar in `g_hammer_tactics.mlg`. `src/lib/` is shared OCaml infrastructure exposed as its own findlib library (`coq-hammer-tactics.lib`): `hhutils.ml` (wrappers around Rocq APIs), `hhlib.ml` (generic utilities), `hhlpo.ml` (lexicographic path order), `hammer_errors.ml`, `hhpartac.ml` (parallel tactic execution). File conventions: `.mlg` files are Rocq grammar extensions (VERNAC/TACTIC EXTEND) preprocessed by coqpp; `.mlpack` files list the modules packed into each plugin. Adding an OCaml file requires updating both the relevant `_CoqProject.*` file and the dune setup. `eval/` contains the benchmark harness for evaluating hammer performance on Coq libraries (see `eval/README.md`). ## Style (from CONTRIBUTING.md) - No TABs, ever — spaces only. - Follow the existing indentation and formatting style. - Remove dead code instead of commenting it out. - Avoid code duplication. Abstract common logic into shared helper functions. - Keep commits focused; avoid unrelated or behavior-neutral changes unless the commit is explicitly a refactor. ## Instructions - When finished, verify with `just check` coqhammer-1.3.3-9.2/CHANGES.md000066400000000000000000000115051522316141700153760ustar00rootroot00000000000000CoqHammer v. 1.3.3 ================== Rocq versions compatibility: 9.1. Overview of changes ------------------- * `hammer [lemma1; ...; lemmaN]` syntax. * Fixed translation regression due to changed qualified Rocq names for logical connectives. * Fixed `dune` build. * Fixed deprecation warnings. * The `unfold`, `unfold!` and `unfolding` options accept notations (e.g. `unfold: "#"`). * Fixed issues #86, #118, #119, #130, #134, #138, #140, #141, #144, #180, #183, #202. CoqHammer v. 1.3.2 ================== Coq versions compatibility: 8.10-9.1. Overview of changes ------------------- * Module filtering with the `Hammer Filter` table. * Fixed issue #106 (`sauto` argument parsing bug). * Fixed issue #108. CoqHammer v. 1.3.1 ================== Coq versions compatibility: 8.10, 8.11, 8.12, 8.13. Overview of changes ------------------- * New `sauto` option shorthands: `b:`, `lb:`, `qb:`, `lqb:`, etc. * The `best` tactic which tries several variants of `sauto` in parallel. * Several variants of `sauto` tried in parallel as the preliminary tactic in `hammer`. * Automatic ATP detection in `hammer`. * Proper handling of implicit arguments with `use:`. * Fixed issue #45. CoqHammer v. 1.3 ================ Coq versions compatibility: 8.10, 8.11, 8.12. Overview of changes ------------------- * Proper argument parsing for the automated reasoning tactics. Change of tactic interface. * Optional boolean reflection in `sauto`. * Hint databases can now be used with `sauto`. * Dependent elimination with `depelim` can now be optionally performed by `sauto` (the `dep:` option). * Simplifications for sigma-types in `sauto`. * Improvements of the `sauto` proof search procedure. * Better failure messages for the tactics. * More readable dependency names (without extra qualifiers). * `sauto` is now the preliminary tactic for `hammer`. * Rudimentary MathComp support. New `make` targets: `mathcomp` and `install-mathcomp`. * Tutorial. Details of the sauto proof search improvements ---------------------------------------------- * Actions modulo head reduction. * Better `sdestruct` behaviour with boolean comparisons. * The `f_equal` action. * A major speedup by removing superfluous rewrite hints. * Speedup by using proper Coq API functions for term comparisons. CoqHammer v. 1.2.1 ================== Coq versions compatibility: 8.10, 8.11. Overview of changes ------------------- * Fixed the "Anomaly" error upon `hammer` failure. CoqHammer v. 1.2 ================ Coq versions compatibility: 8.10, 8.11. Overview of changes ------------------- * New reconstruction backend. The reconstruction tactics are now based on a reasonably general proof search procedure for the Calculus of Inductive Constructions and are more useful independently. * Bugfixes in the `predict` program: now compiles with recent versions of GCC and works correctly on macOS. CoqHammer v. 1.1.1 ================== Coq versions compatibility: 8.9, 8.10. Overview of changes ------------------- * Separate packaging of the plugin and the reconstruction tactics. * Quick plugin and tactics tests which do not require ATP provers installed (`make quicktest`, `make test-plugin`, `make test-tactics`). * Machine-learning features now take into account the polarity (positive/negative) of symbol occurrences (`opt_feature_polarity`). * Opaqueness information now taken into account with constant unfolding. CoqHammer v. 1.1 ================ Coq versions compatibility: 8.8, 8.9. Overview of changes ------------------- * CVC4 integration. * Minimization of dependencies. * Parallel invocation of proof tactics. * More reliable timeout mechanism based on `fork` and `wait`. * Improvements in the reconstruction tactics, more rewrite hints for `sauto`. * Change in reconstruction tactics interface. Tactics no longer need a list of hypotheses, and a different set of tactics is used. * Improvements in the translation. * Messages now more user-friendly. * `predict` tactic. * Added `opam` support. * More consistent removal of temporary files. * Debugging commands. * Tests (`make tests`). Technical details of improvements to the translation ---------------------------------------------------- * Hashing of lifted-out terms. * Type lifting (`opt_type_lifting`): hashing of types and lifting them out, e.g., ```coq forall f : nat -> nat, g : (nat -> nat) -> nat -> nat, ... ``` is translated to ```coq forall f, T1(f) -> forall g, T2(g) -> ... ``` with axioms ```coq forall f, T1(f) <-> forall x, nat(x) -> nat(f x) forall g, T2(g) <-> forall h, T1(h) -> forall x, nat(x) -> nat(g h x) ``` instead of translating this to ```coq forall f, (forall x, nat(x) -> nat(f x)) -> forall g, (forall h, (forall x, nat(x) -> nat(h x))) -> forall x, nat(x) -> nat(g h x)) -> ... ``` * `Set` now collapsed to `Type` CoqHammer v. 1.0 ================ Coq versions compatibility: 8.6. First full CoqHammer version. coqhammer-1.3.3-9.2/CLAUDE.md000077700000000000000000000000001522316141700165572AGENTS.mdustar00rootroot00000000000000coqhammer-1.3.3-9.2/CONTRIBUTING.md000066400000000000000000000040251522316141700162340ustar00rootroot00000000000000This file contains a few simple general rules for keeping code clean, which are not difficult to apply and save a lot of effort later. Please, read all points and try to follow. 1. Do *not* use TABs, under any circumstances. Set your editor to automatically convert them to spaces. 2. Make sensible indentation. Do *not* use TABs. 3. Within reason, try to follow the coding style of the code already present in the repository, i.e., the same kind of indentation (number of spaces), the same way of inserting newlines, etc. 4. As a general rule, avoid copy & paste. Instead, abstract out a more general parameterised function. 5. Try to make commits which include only things directly relevant to what you're changing. Do not make changes which do nothing (which don't change the behaviour of the code), unless your commit is explicitly about refactoring (cleaning up) code. Avoid including outcommented code, changes of parameters you just used for debugging, hardcoded paths, etc. **Hint**: use `git diff` to review your changes before committing. 6. Try to split commits that do many unrelated things into several commits, each doing one thing. Splitting commits might not always be worth the effort, but it's always worth trying to do this and to keep it in mind. Then the diffs are easy to read, and you can easily find what was changed when and for what purpose. 7. Remove unused code, don't comment it out. With git you can always go back, and really removing things shows up on diffs. 8. When starting on a new thing make a branch (`git branch`, `git checkout -b`) from the most recent development version for a stable version of Coq. This will be in one of the coq8.X branches (ask if not sure). This is *never* the master branch. Branching out from master is bad because the master branch is synchronised with the most recent unstable development version of Coq, which constantly changes and you're then suddenly no longer able to compile things you wrote a few days/weeks/months ago. coqhammer-1.3.3-9.2/CREDITS.md000066400000000000000000000015471522316141700154300ustar00rootroot00000000000000Main authors ------------ * Lukasz Czajka * Logic-related components: translation, proof reconstruction, automated reasoning tactics. Author of almost all OCaml/Ltac/Coq code. Author of the `sauto` tactic. * Cezary Kaliszyk * Machine-learning component: premise selection. Author of the `predict` program. Other contributors ------------------ * Burak Ekici * Preliminary version of boolean reflection in `sauto`. * CVC4 integration. * Evan Marzion * First version of hashing of lifted-out terms in the translation. * Thibault Gauthier * Preliminary version of Coq data export. * Ping Hou * Testing of the `sauto` tactic. * Karl Palmskog * Opam packaging, Travis CI configuration and Dune build scripts. * Other contributors listed on GitHub * Small bugfixes and keeping up-to-date with Coq master. coqhammer-1.3.3-9.2/LICENSE000066400000000000000000000576471522316141700150320ustar00rootroot00000000000000COPYRIGHT Copyright (c) 2017, Łukasz Czajka and Cezary Kaliszyk, University of Innsbruck LICENSE GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS coqhammer-1.3.3-9.2/Makefile000066400000000000000000000056401522316141700154470ustar00rootroot00000000000000 BINDIR ?= $(if $(COQBIN),$(COQBIN),`rocq c -where | xargs dirname | xargs dirname`/bin/) default: all all: $(MAKE) tactics $(MAKE) install-tactics $(MAKE) plugin tactics: Makefile.coq.tactics -rm -f META $(MAKE) -f Makefile.coq.tactics plugin: Makefile.coq.plugin Makefile.coq.plugin.local -rm -f META $(MAKE) -f Makefile.coq.plugin mathcomp: Makefile.coq.mathcomp $(MAKE) -f Makefile.coq.mathcomp install: install-tactics install-plugin install-tactics: tactics $(MAKE) -f Makefile.coq.tactics install install-plugin: plugin $(MAKE) -f Makefile.coq.plugin install install-mathcomp: Makefile.coq.mathcomp $(MAKE) -f Makefile.coq.mathcomp install uninstall: uninstall-tactics uninstall-plugin uninstall-tactics: Makefile.coq.tactics $(MAKE) -f Makefile.coq.tactics uninstall uninstall-plugin: Makefile.coq.plugin Makefile.coq.plugin.local $(MAKE) -f Makefile.coq.plugin uninstall uninstall-mathcomp: Makefile.coq.mathcomp $(MAKE) -f Makefile.coq.mathcomp uninstall Makefile.coq.plugin: _CoqProject.plugin rocq makefile -f _CoqProject.plugin -o Makefile.coq.plugin Makefile.coq.tactics: _CoqProject.tactics rocq makefile -f _CoqProject.tactics -o Makefile.coq.tactics Makefile.coq.mathcomp: _CoqProject.mathcomp rocq makefile -f _CoqProject.mathcomp -o Makefile.coq.mathcomp tests: tests-plugin tests-tactics tests-plugin: $(MAKE) -B -C tests/plugin tests-tactics: $(MAKE) -B -C tests/tactics quicktest: test-plugin test-tactics test-plugin: $(MAKE) -B -C tests/plugin plugin_test.vo test-tactics: $(MAKE) -B -C tests/tactics tactics_test.vo clean: Makefile.coq.tactics Makefile.coq.plugin Makefile.coq.plugin.local Makefile.coq.mathcomp $(MAKE) -f Makefile.coq.tactics cleanall -$(MAKE) -f Makefile.coq.plugin cleanall -$(MAKE) -f Makefile.coq.mathcomp cleanall -rm -rf _build rm -f Makefile.coq.tactics Makefile.coq.tactics.conf Makefile.coq.plugin Makefile.coq.plugin.conf Makefile.coq.mathcomp Makefile.coq.mathcomp.conf META dune: dune-tactics dune-plugin dune-tactics: dune build -p coq-hammer-tactics dune-plugin: dune build -p coq-hammer-tactics,coq-hammer dune-install: dune-install-tactics dune-install-plugin dune-install-tactics: dune-tactics dune install coq-hammer-tactics dune-install-plugin: dune-plugin dune install coq-hammer dune-uninstall: dune uninstall coq-hammer coq-hammer-tactics dune-uninstall-tactics: dune uninstall coq-hammer-tactics dune-uninstall-plugin: dune uninstall coq-hammer dune-clean: dune clean $(MAKE) -C eval clean $(MAKE) -C tests/plugin clean $(MAKE) -C tests/tactics clean .PHONY: default all tactics plugin mathcomp install install-tactics install-plugin install-mathcomp uninstall uninstall-tactics uninstall-plugin tests tests-plugin tests-tactics quicktest test-plugin test-tactics clean dune dune-tactics dune-plugin dune-install dune-install-tactics dune-install-plugin dune-clean install-extra dune-uninstall dune-uninstall-tactics dune-uninstall-plugin coqhammer-1.3.3-9.2/Makefile.coq.plugin.local000066400000000000000000000014721522316141700206150ustar00rootroot00000000000000COQ_SRC_SUBDIRS+=user-contrib/Hammer/Tactics CAMLPKGS+= -package coq-hammer-tactics.lib post-all:: predict htimeout predict: src/predict/main.cpp src/predict/predictor.cpp src/predict/format.cpp src/predict/knn.cpp src/predict/nbayes.cpp src/predict/rforest.cpp src/predict/tfidf.cpp src/predict/dtree.cpp c++ -std=c++11 -DCOQ_MODE -O2 -Wall src/predict/main.cpp -o predict htimeout: src/htimeout/htimeout.c cc -O2 -Wall src/htimeout/htimeout.c -o htimeout BINDIR ?= $(if $(COQBIN),$(COQBIN),`rocq c -where | xargs dirname | xargs dirname`/bin/) install-extra:: install -d $(DESTDIR)$(BINDIR) install -m 0755 predict $(DESTDIR)$(BINDIR)predict install -m 0755 htimeout $(DESTDIR)$(BINDIR)htimeout clean:: rm -f predict htimeout $(MAKE) -C eval clean $(MAKE) -C tests/plugin clean $(MAKE) -C tests/tactics clean coqhammer-1.3.3-9.2/Makefile.coq.plugin.local-late000066400000000000000000000011101522316141700215250ustar00rootroot00000000000000# coq_makefile's default findlib_remove delegates to `ocamlfind remove`. # That is not robust when switching from a dune/opam install to this Makefile: # dune can leave subdirectories such as plugin/ in the package directory, and # ocamlfind refuses to remove those directories. # # The install rule immediately reinstalls the same package, so make removal # idempotent by deleting the whole package directory in the selected destdir. findlib_remove = \ $(HIDE)if [ -n "$(METAFILE)" ] && [ -n "$(FINDLIBPACKAGE)" ]; then \ rm -rf "$(COQPLUGININSTALL)/$(FINDLIBPACKAGE)"; \ fi coqhammer-1.3.3-9.2/Makefile.coq.tactics.local-late000066400000000000000000000011211522316141700216630ustar00rootroot00000000000000# coq_makefile's default findlib_remove delegates to `ocamlfind remove`. # That is not robust when switching from a dune/opam install to this Makefile: # dune can leave subdirectories such as lib/ and plugin/ in the package # directory, and ocamlfind refuses to remove those directories. # # The install rule immediately reinstalls the same package, so make removal # idempotent by deleting the whole package directory in the selected destdir. findlib_remove = \ $(HIDE)if [ -n "$(METAFILE)" ] && [ -n "$(FINDLIBPACKAGE)" ]; then \ rm -rf "$(COQPLUGININSTALL)/$(FINDLIBPACKAGE)"; \ fi coqhammer-1.3.3-9.2/README.md000066400000000000000000000032071522316141700152630ustar00rootroot00000000000000CoqHammer 1.3.3 for Rocq 9.2 [![Docker CI][docker-action-shield]][docker-action-link] [docker-action-shield]: https://github.com/lukaszcz/coqhammer/actions/workflows/docker-action.yml/badge.svg?branch=v1.3.3-rocq9.2 [docker-action-link]: https://github.com/lukaszcz/coqhammer/actions?query=workflow:"Docker%20CI" CoqHammer video tutorial: [part 1 (sauto)](https://www.youtube.com/watch?v=0c_utk9bVgU&list=PLXXF_svQE_b-9A5p2OKU7Tjz-NcE7H2xg), [part 2 (hammer)](https://www.youtube.com/watch?v=EEmpVCSqShA&list=PLXXF_svQE_b_vja6TWFbGNB266Et8m5yC). Since version 1.3, the CoqHammer system consists of two major separate components. 1. The `sauto` general proof search tactic for the Calculus of Inductive Construction. 2. The `hammer` automated reasoning tool which combines learning from previous proofs with the translation of problems to the logics of external automated systems and the reconstruction of successfully found proofs with the `sauto` procedure. See the [CoqHammer webpage](https://coqhammer.github.io) for documentation and installation instructions. Requirements ------------ - [Rocq 9.2](https://rocq-prover.org/) - for `hammer`: automated provers ([Vampire](https://vprover.github.io/download.html), [CVC4](http://cvc4.cs.stanford.edu/downloads/), [Eprover](http://www.eprover.org), and/or [Z3](https://github.com/Z3Prover/z3/releases)) Copyright and license --------------------- Copyright (c) 2017-2025, Lukasz Czajka.\ Copyright (c) 2017-2018, Cezary Kaliszyk, University of Innsbruck. Distributed under the terms of LGPL 2.1, see the file [LICENSE](LICENSE). See [CREDITS](CREDITS.md) for a full list of contributors. coqhammer-1.3.3-9.2/TODO.md000066400000000000000000000144321522316141700150750ustar00rootroot00000000000000Problems -------- 1. Make boolean reflection work. Make CoqHammer usable with MathComp: this will probably require much more than just making boolean reflection work, probably including most of the points below. 2. Omit (some) type arguments (inductive type parameters? implicit type arguments?) to polymorphic functions/constructors (e.g. cons). Is it possible to determine which arguments are implicit at the Coq kernel level? Yes: `Impargs.implicits_of_global`. The easy thing to do first is to just omit the arguments declared as implicit. Then try inductive type parameters? Think about other possibilities. 3. Omit (some) type guards when the type may be inferred. For example, * forall x : nat, Even(x) -> phi probably may be translated to * forall x, Even(x) -> phi', because Even(x) implies nat(x). A non-trivial problem is to precisely formulate a general criterion, and prove it correct for a reasonable subset of CIC. 4. (partly done) For reconstruction: look at the inversion (also discrimination, injection -- less useful?) axioms used in the ATP proofs and add them to the context before invoking a reconstruction tactic. Or use the inversion axioms to specify the "inverting" option of the reconstruction tactics. Make some intelligent use of other information contained in the atp_info data structure (src/plugin/provers.mli). Also look at the axioms for matches, which may sometimes be used by the ATPs to do inversion (see point 7). Try to use even more information from ATP runs. Dig deeper into ATP proofs. 5. Heuristic monomorphisation (instantiation of polymorphic definitions with types). It is important to do this on the translation level and not leave it to the ATPs, because then the translation output may be further optimised. For example, * forall (A : Type) (x : A), phi is translated to * forall A, T(A, Type) -> forall x, T(x, A) -> phi', but in an instantiated version the type guards may be optimised, e.g. for instantiation with nat to: * forall x, nat(x) -> phi'. The monomorphisation is especially important for higher-order statements, whose translations are now not very usable by the ATPs. See e.g. the inversion axiom for List.Forall (Hammer_transl "List.Forall"). 6. Optimise type guards for parameterised types. For instance, forall x : list nat, phi is translated to * forall x, T(x, list nat) -> phi', but should be to * forall x, list_nat(x) -> phi'. This will work well in combination with heuristic monomorphisation. The above example of list with nat parameter is simple, but types in Coq can be complicated. Can we do something when some of the type parameters contain occurrences of variables bound externally? For example: * forall x y, T(x, A) -> T(y, list (Q x)) -> phi. We can, e.g., have an optimised type guard list(Q x, y) or list\_Q(x,y). What are other possibilities? What if T(y, list (Q nat)), maybe then list\_Q\_nat(y)? This problem probably involves much experimentation trying to figure out the right way of doing this. 7. Try breaking up the axiom for matches into one axiom for each constructor. E.g. instead of translating * match x with 0 => t1 | S y => t2 end to: * forall x, nat(x) -> (x = 0 /\ F x = t1') \\/ (exists y, nat(y) /\ x = S y /\ F x = t2') use two axioms: 1. F 0 = t1'[0/x] 2. forall y, nat(y) -> F (S(y)) = t2'[S(y)/x] Note that in point 2 the guard nat(y) should be omitted if `opt_closure_guards` is false (this is analogous to omitting type guards for free variables of lambda-lifted expressions). This is related to program extraction. See Pierre Letouzey’s Ph.D. thesis. 8. Try giving symbol ordering hints to ATPs. There is a natural order on constants: c1 > c2 if transitive-closure(c2 occurs in the definition of c1). This ordering, lifted to lexicographic path order, seems to work well in the reconstruction tactics. See src/lib/lpo.ml and the implementation of rewriting actions in src/tactics/sauto.ml. Extend this idea, try different orderings. 9. Properly handle functions which use dependent types in a non-trivial way. Properly handle case analysis for small propositional inductive types. Properly handle sig, sigT, etc., and prod, sum, etc. with propositional arguments. For example, given ```coq Definition h (x y z : nat) (p : x = y /\ y = z) : {u : nat | x = u} := match p with | conj p1 p2 => exist (fun u => x = u) z (eq_trans p1 p2) end. ``` the function `h` has type ```coq forall x y z : nat, x = y /\ y = z -> {u : nat | x = u} ``` It should be translated to a definition of a function `h` * forall x y z, h(x, y, z) = z and a specification axiom derived from the type * forall x y z, x = y /\ y = z -> x = h(x, y, z) Currently, no function definition for h is generated. Neither is the specification axiom. Only an unusable typing axiom for h is generated. A similar problem is considered in Pierre Letouzey’s Ph.D. thesis, but there the goal is only code extraction, so there is no need to generate the specification axioms derived from types. In addition to program extraction, we need to do *specification extraction*. 10. Explicitly state the types of non-trivial terms. E.g. if f:nat->nat and 0:nat and (f 0) occurs (in the goal or hypothesis?) then state (f 0):nat as an axiom. More general: consider non-trivial terms as possible premises. This ties in with monomorphisation. What types to choose for instantiating e.g. list? Do machine-learning premise selection with (list nat), (list Z), etc. among premises. 11. Improvements in premise selection: better features, other algorithms? Special status for head constants? 12. Translation to HOL. Factor the translation, including a HOL intermediate stage: Coq -> CIC_0 -> HOL -> applicative FOL -> FOL. Try using higher-order ATPs. 13. Write a custom version of the `eapply` tactic which does unification modulo "simple" (equational?) reasoning. See the smart matching of Matita. 14. Optional use of classical logic. Technical improvements ---------------------- 1. Remove dependence on "grep". 2. Make the plugin work on Windows. coqhammer-1.3.3-9.2/_CoqProject.mathcomp000066400000000000000000000000771522316141700177500ustar00rootroot00000000000000-Q theories/Tactics Hammer.Tactics theories/Tactics/Mathcomp.v coqhammer-1.3.3-9.2/_CoqProject.plugin000066400000000000000000000013551522316141700174360ustar00rootroot00000000000000src/plugin/META.coq-hammer -Q theories/Plugin Hammer.Plugin -Q src/plugin Hammer.Plugin -I src/plugin src/plugin/hh_term.ml src/plugin/msg.ml src/plugin/timeout.ml src/plugin/coq_transl_opts.ml src/plugin/coqterms.ml src/plugin/defhash.mli src/plugin/defhash.ml src/plugin/coq_typing.mli src/plugin/coq_typing.ml src/plugin/hashing.mli src/plugin/hashing.ml src/plugin/coq_convert.mli src/plugin/coq_convert.ml src/plugin/tptp_out.mli src/plugin/tptp_out.ml src/plugin/coq_transl.mli src/plugin/coq_transl.ml src/plugin/opt.ml src/plugin/parallel.ml src/plugin/features.mli src/plugin/features.ml src/plugin/provers.mli src/plugin/provers.ml src/plugin/hammer_main.ml src/plugin/g_hammer.mlg src/plugin/hammer_plugin.mlpack theories/Plugin/Hammer.v coqhammer-1.3.3-9.2/_CoqProject.tactics000066400000000000000000000012471522316141700175720ustar00rootroot00000000000000src/tactics/META.coq-hammer-tactics -R theories/Tactics Hammer.Tactics -R src/lib Hammer.Tactics -R src/tactics Hammer.Tactics -I src/lib -I src/tactics src/lib/hammer_errors.ml src/lib/hhutils.mli src/lib/hhutils.ml src/lib/hhlib.ml src/lib/hhlpo.mli src/lib/hhlpo.ml src/lib/hhpartac.ml src/lib/g_hammer_lib.mlg src/lib/hammer_lib.mlpack src/tactics/sauto.mli src/tactics/sauto.ml src/tactics/tacopts.mli src/tactics/tacopts.ml src/tactics/tacbest.mli src/tactics/tacbest.ml src/tactics/tactics_main.ml src/tactics/g_hammer_tactics.mlg src/tactics/hammer_tactics.mlpack theories/Tactics/Reconstr.v theories/Tactics/Reflect.v theories/Tactics/Tactics.v theories/Tactics/Hints.v coqhammer-1.3.3-9.2/coq-hammer-tactics.opam000066400000000000000000000017301522316141700203420ustar00rootroot00000000000000opam-version: "2.0" version: "1.3.3+9.2" maintainer: "lukaszcz@mimuw.edu.pl" homepage: "https://github.com/lukaszcz/coqhammer" dev-repo: "git+https://github.com/lukaszcz/coqhammer.git" bug-reports: "https://github.com/lukaszcz/coqhammer/issues" license: "LGPL-2.1-only" synopsis: "Reconstruction tactics for the hammer for Coq" description: """ Collection of tactics that are used by the hammer for Coq to reconstruct proofs found by automated theorem provers. When the hammer has been successfully applied to a project, only this package needs to be installed; the hammer plugin is not required. """ build: [make "-j%{jobs}%" "tactics"] install: [ [make "install-tactics"] [make "test-tactics"] {with-test} ] depends: [ "ocaml" {>= "4.09.0"} "rocq-core" {>= "9.2" & < "9.3~"} "rocq-stdlib" {>= "9.1" & < "9.3~"} ] tags: [ "keyword:automation" "keyword:hammer" "keyword:tactics" "logpath:Hammer.Tactics" ] authors: [ "Lukasz Czajka " ] coqhammer-1.3.3-9.2/coq-hammer.opam000066400000000000000000000021311522316141700167060ustar00rootroot00000000000000opam-version: "2.0" version: "1.3.3+9.2" maintainer: "lukaszcz@mimuw.edu.pl" homepage: "https://github.com/lukaszcz/coqhammer" dev-repo: "git+https://github.com/lukaszcz/coqhammer.git" bug-reports: "https://github.com/lukaszcz/coqhammer/issues" license: "LGPL-2.1-only" synopsis: "General-purpose automated reasoning hammer tool for Coq" description: """ A general-purpose automated reasoning hammer tool for Coq that combines learning from previous proofs with the translation of problems to the logics of automated systems and the reconstruction of successfully found proofs. """ build: [make "-j%{jobs}%" "plugin"] install: [ [make "install-plugin"] [make "test-plugin"] {with-test} ] depends: [ "ocaml" {>= "4.09.0"} "rocq-core" {>= "9.2" & < "9.3~"} "rocq-stdlib" {>= "9.1" & < "9.3~"} ("conf-g++" {build} | "conf-clang" {build}) "coq-hammer-tactics" {= version} ] tags: [ "category:Miscellaneous/Coq Extensions" "keyword:automation" "keyword:hammer" "logpath:Hammer.Plugin" ] authors: [ "Lukasz Czajka " "Cezary Kaliszyk " ] coqhammer-1.3.3-9.2/dune000066400000000000000000000006741522316141700146670ustar00rootroot00000000000000(env (dev (flags (:standard -w -27 -w -3)))) (rule (targets predict) (deps (sandbox always) (source_tree src/predict)) (action (run c++ -std=c++11 -DCOQ_MODE -O2 -Wall src/predict/main.cpp -o predict))) (rule (targets htimeout) (deps (sandbox always) (source_tree src/htimeout)) (action (run cc -O2 -Wall src/htimeout/htimeout.c -o htimeout))) (install (files predict htimeout) (section bin) (package coq-hammer)) coqhammer-1.3.3-9.2/dune-project000066400000000000000000000000441522316141700163220ustar00rootroot00000000000000(lang dune 3.21) (using rocq 0.11) coqhammer-1.3.3-9.2/eval/000077500000000000000000000000001522316141700147315ustar00rootroot00000000000000coqhammer-1.3.3-9.2/eval/Makefile000066400000000000000000000024661522316141700164010ustar00rootroot00000000000000# input files FFILES=$(shell find problems/ -name "*.v" | sort -R) OFILES=$(patsubst problems/%.v,problems/%.vo,$(FFILES)) COQC=rocq c $(shell find problems/ -name "*.conf" -exec cat {} + | tr "\n" " ") all: @echo "See README on how to invoke make." init: $(OFILES) problems/%.vo: problems/%.v @mkdir -p logs/init $(COQC) "$<" > logs/init/`basename "$@" .vo`.log 2>&1 check: $(patsubst problems/%.v,logs/check/%.log,$(FFILES)) logs/check/%.log: problems/%.v @mkdir -p `dirname "$@"` $(COQC) "$<" > "$@" 2>&1 atp: $(patsubst problems/%.v,logs/atp/%.log,$(FFILES)) logs/atp/%.log: problems/%.v @mkdir -p `dirname "$@"` $(COQC) "$<" > "$@" 2>&1 reconstr: $(patsubst problems/%.v,logs/reconstr/%.log,$(FFILES)) logs/reconstr/%.log: problems/%.v @mkdir -p `dirname "$@"` $(COQC) "$<" > "$@" 2>&1 prove: $(patsubst problems/%.v,logs/prove/%.log,$(FFILES)) logs/prove/%.log: problems/%.v @mkdir -p `dirname "$@"` $(COQC) "$<" > "$@" 2>&1 clean-vo: rm -f $(OFILES) clean: clean-vo rm -rf logs coqhammer.opt check.log gen_atp.log $(MAKE) -C tools clean clean-problems: clean-vo rm -f $(patsubst problems/%.v,problems/.%.aux,$(FFILES)) rm -f $(patsubst problems/%.v,problems/%.v.bak,$(FFILES)) rm -f $(patsubst problems/%.v,problems/%.glob,$(FFILES)) .PHONY: clean clean-vo clean-problems check atp reconstr init all coqhammer-1.3.3-9.2/eval/README.md000066400000000000000000000067101522316141700162140ustar00rootroot00000000000000How to evaluate a new Coq library? ---------------------------------- Let `N` be the number of parallel jobs to execute. Unless otherwise stated, execute all commands in the `eval/` directory. Some libraries prepared for evaluation are available at https://github.com/lukaszcz/coqhammer-eval.git. If the library to evaluate is already prepared (according to steps 1-6 below), then put it in the `problems/` subdirectory and do: ```bash ./run-eval.sh N [your.mail@mail.com] ``` Otherwise follow all steps below. You may find `make clean-problems` useful when you want to redo some steps. 1. Place the library sources in the `problems/` directory (possibly with subdirectories). The sources should contain the `*.v` files. 2. `cd tools && make` 3. Run `tools/fixreqs.sh prefix` in the `problems/` directory to fix the `Require` statements. This script expects one parameter -- the Coq logical prefix for the library. All `Require file` (also `Require Import` and `Require Export`) statements for files which are found in the `problems/` directory are changed to `From prefix Require file`. 4. `make -j N init` This will compile the problems, creating the necessary `*.glob` files. If some files do not compile then you need to fix this manually. 5. `cd problems && ../tools/mkhooks.sh` This script may be used to insert calls to `hammer_hook` in the library source files (it requires the corresponding `*.glob` files to be present). Run it in the `problems/` directory. After running `tools/mkhooks.sh` you may need to edit some files manually to make them compile with `coqc`. 6. `./check.sh N` This checks if the problems compile with `coqc` after running `tools/mkhooks.sh`. It may fail for some files, which must be then edited manually to make them compile with `coqc`. The errors may be viewed in the `check.log` file. 7. `./gen-atp.sh N [your.mail@mail.com]` After running this command the generated ATP problems are in the `atp/problems/` directory. 8. `cd atp && ./run-provers.sh N [your.mail@mail.com]` The script `atp/run-provers.sh` should be edited when adding or changing the (versions of) ATP provers used in the evaluation. When adding new ATPs also the `hammer_hook` code in [`src/plugin/hammer_main.ml`](../src/plugin/hammer_main.ml) should be edited. 9. `./run-reconstr.sh N [your.mail@mail.com]` After executing these steps, the reconstruction results are in the `out/` directory. The ATP results are in the `atp/o/` directory. 10. `./gen-stats.sh` This computes the statistics (including the greedy sequence), using the `stat` program (see below). Steps 7-10 may be run using the script `./run-eval.sh [-v] N [your.mail@mail.com]`. The optional flag -v enables the verbose mode (more emails about the progress are sent). Tools ----- * `stat`: compute ATP statistics. Run in the `atp/` directory (or `eval/` with the `-r` option). Reads the `o/*/*.p` files (`out/*/*.out` with the `-r` option). Example: `tools/stat , y,p , , false` `stat` takes 5 (optionally 6) space-separated arguments: the `-r` option (optional), 4 lists (comma-separated values; empty list is represented by a single comma) and a boolean ``` stat -r [labels] [sorting specification] [which fields to merge] [greedy sequence fixed start] (should different versions of the greedy sequence be computed?) ``` - `y` - the number of proved theorems - `n` - the number of countersatisfiable problems - `p` - the prover coqhammer-1.3.3-9.2/eval/atp/000077500000000000000000000000001522316141700155155ustar00rootroot00000000000000coqhammer-1.3.3-9.2/eval/atp/Makefile000066400000000000000000000016101522316141700171530ustar00rootroot00000000000000# # ATP evaluation # # See README for details. # # input files FFILES=$(shell find i/f/ -type f | sort -R) # timeout (in seconds) TIM=30 all: eprover vampire z3 cvc4 eprover: $(patsubst i/f/%,o/eprover/%,$(FFILES)) o/eprover/%: i/f/% @mkdir -p `dirname "$@"` @eprover -s --cpu-limit=$(TIM) --auto-schedule -R --print-statistics -p --tstp-format "$<" | grep "file[(]'\|# SZS" > "$@" vampire: $(patsubst i/f/%,o/vampire/%,$(FFILES)) o/vampire/%: i/f/% @mkdir -p `dirname "$@"` @htimeout $(TIM) vampire --mode casc -t $(TIM) --proof tptp --output_axiom_names on "$<" | grep "file[(]'\|% SZS" > "$@" z3: $(patsubst i/f/%,o/z3/%,$(FFILES)) o/z3/%: i/f/% @mkdir -p `dirname "$@"` @htimeout $(TIM) z3_tptp -c -t:$(TIM) "-file:$<" > "$@" cvc4: $(patsubst i/f/%,o/cvc4/%,$(FFILES)) o/cvc4/%: i/f/% @mkdir -p `dirname "$@"` @htimeout $(TIM) cvc4 --tlimit $(TIM) --dump-unsat-cores-full "$<" > "$@" coqhammer-1.3.3-9.2/eval/atp/README.md000066400000000000000000000013651522316141700170010ustar00rootroot00000000000000ATP performance evaluation -------------------------- Given a set of ATP problems, the Makefile in this directory runs ATPs to determine which problems ATPs are able to solve within a given time. The input files need to be in subdirectories of `i`: * `i/f` should contain all the FOF files to evaluate. * `i/h` may contain all the THF files to evaluate. * `i/w` may contain all the Why3 files to evaluate. The outputs are written to `o/$prover_name/...` To run, use `make -j 47 -k` (where 47 is the number of CPUs). The parameter `-k` resumes evaluation in case of errors. The timeout can be set in the Makefile. Optionally only particular provers may be specified, e.g. `make eprover`. Warning: Rerun `make` to ensure that all problems were treated. coqhammer-1.3.3-9.2/eval/atp/run-provers.sh000077500000000000000000000006751522316141700203660ustar00rootroot00000000000000#!/bin/bash mkdir i for d in problems/* do echo "***************" echo $d rm -f i/f ln -s ../$d i/f make -k -j "$1" eprover vampire z3 cvc4 p=`basename $d` mv o/eprover o/eprover-$p mv o/vampire o/vampire-$p mv o/z3 o/z3-$p mv o/cvc4 o/cvc4-$p if [ -n "$2" ]; then echo "" | mail -s "provers $p finished" "$2" fi done if [ -n "$2" ]; then echo "" | mail -s "Provers finished" "$2" fi coqhammer-1.3.3-9.2/eval/check.sh000077500000000000000000000003141522316141700163430ustar00rootroot00000000000000#!/bin/bash echo "check" > coqhammer.opt rm -rf logs/check/ rm check.log make -k -j "$1" check 2>&1 | tee check.log mv check.log check.log.bak cat check.log.bak | grep Error > check.log rm check.log.bak coqhammer-1.3.3-9.2/eval/gen-atp.sh000077500000000000000000000005011522316141700166170ustar00rootroot00000000000000#!/bin/bash echo "gen-atp" > coqhammer.opt rm -rf logs/atp/ rm -rf atp/problems rm gen-atp.log make -k -j "$1" atp 2>&1 | tee gen-atp.log mv gen-atp.log gen-atp.log.bak cat gen-atp.log.bak | grep Error > gen-atp.log rm gen-atp.log.bak if [ -n "$2" ]; then echo "" | mail -s "ATP problem generation finished" "$2" fi coqhammer-1.3.3-9.2/eval/gen-stats.sh000077500000000000000000000101031522316141700171700ustar00rootroot00000000000000#!/bin/bash cd tools make cd .. cd atp ../tools/stat , y,p , , false cd .. tools/stat -r , y,p , , false echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "
hautoxeautoscrushqcrush
" >> statistics.html echo `find out -name "*.out" -exec grep 'hauto$' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep xeauto {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep scrush {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep 'qcrush$' {} + | wc -l` >> statistics.html echo "
" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "
leautoqproversyellessreconstr
" >> statistics.html echo `find out -name "*.out" -exec grep 'leauto' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep qprover {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep 'syelles' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep sreconstr {} + | wc -l` >> statistics.html echo "
" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "
qblastsblastqcrush2hcrush
" >> statistics.html echo `find out -name "*.out" -exec grep 'qblast' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep 'sblast' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep 'qcrush2' {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep hcrush {} + | wc -l` >> statistics.html echo "
" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "
rryellesrrcrushrryreconstrrrblast
" >> statistics.html echo `find out -name "*.out" -exec grep rryelles {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep rrcrush {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep rryreconstr {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep rrblast {} + | wc -l` >> statistics.html echo "
" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "" >> statistics.html echo "
rfirstorderxeautortautoreasy
" >> statistics.html echo `find out -name "*.out" -exec grep rfirstorder {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep xeauto {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep rtauto {} + | wc -l` >> statistics.html echo "" >> statistics.html echo `find out -name "*.out" -exec grep reasy {} + | wc -l` >> statistics.html echo "
" >> statistics.html coqhammer-1.3.3-9.2/eval/gen-tests.sh000077500000000000000000000002541522316141700172020ustar00rootroot00000000000000#!/bin/bash rm -f problems || rm -rf problems cd tests make clean cd .. cp -r tests problems echo "check" > coqhammer.opt cd tests make -k -j "$1" cd .. ./gen-atp.sh "$1" coqhammer-1.3.3-9.2/eval/run-eval.sh000077500000000000000000000002641522316141700170230ustar00rootroot00000000000000#!/bin/bash ./gen-atp.sh $1 $2 cd atp ./run-provers.sh $1 $2 cd .. ./run-reconstr.sh $1 $2 ./gen-stats.sh if [ -n "$2" ]; then echo "" | mail -s "Evaluation finished" "$2" fi coqhammer-1.3.3-9.2/eval/run-prove.sh000077500000000000000000000004031522316141700172220ustar00rootroot00000000000000#!/bin/bash make clean-vo echo "prove" > coqhammer.opt make -k -j "$1" prove echo -n "Total problems: " ls out/*.out | wc -l echo -n "Successes: " grep "^Success " out/*.out | wc -l if [ -n "$2" ]; then echo "" | mail -s "Coq proving finished" "$2" fi coqhammer-1.3.3-9.2/eval/run-reconstr.sh000077500000000000000000000002651522316141700177340ustar00rootroot00000000000000#!/bin/bash make clean-vo echo "reconstr" > coqhammer.opt make -k -j `echo "($1-4)/4+1" | bc` reconstr if [ -n "$2" ]; then echo "" | mail -s "Reconstruction finished" "$2" fi coqhammer-1.3.3-9.2/eval/tools/000077500000000000000000000000001522316141700160715ustar00rootroot00000000000000coqhammer-1.3.3-9.2/eval/tools/Makefile000066400000000000000000000006521522316141700175340ustar00rootroot00000000000000all: stat coqnames rmcomments fixreqs rm -f *.cm* *.o stat: utils.ml stat.ml ocamlopt -inline 100 -unsafe unix.cmxa str.cmxa $^ -o $@ coqnames: utils.ml coqnames.ml ocamlopt -inline 100 -unsafe unix.cmxa str.cmxa $^ -o $@ fixreqs: utils.ml fixreqs.ml ocamlopt -inline 100 -unsafe unix.cmxa str.cmxa $^ -o $@ rmcomments: rmcomments.c gcc $^ -o $@ clean: -rm -f rmcomments coqnames fixreqs stat *.cmo *.cmi *.cmx *.o coqhammer-1.3.3-9.2/eval/tools/coqnames.ml000066400000000000000000000147451522316141700202440ustar00rootroot00000000000000(* The program to extract "theorems" from Coq source code *) module Lib = Utils let is_idchar = function 'A'..'Z'|'a'..'z'|'0'..'9'|'_'|'\'' -> true | _ -> false let get_name s n = let len = String.length s in let rec pom s n = if n >= len then n else if is_idchar (String.get s n) then pom s (n + 1) else n in if n >= len then "" else let k = pom s n in String.sub s n (k - n) let find_dot s i = let len = String.length s in let rec pom j in_quote = if j >= len then j else if (not in_quote) && String.get s j = '.' then j + 1 else pom (j + 1) (if String.get s j = '\"' then not in_quote else in_quote) in pom i false let remove_hammer_hook s = try let i = Str.search_forward (Str.regexp "hammer_hook ") s 0 in let len = String.length s in let k = find_dot s i in String.sub s 0 i ^ String.sub s k (len - k) with Not_found -> s let process_file fname = let nametab = Hashtbl.create 64 in let create_nametab tfname = let rec pom prefix ic = begin try let s = input_line ic in let i = String.index s ' ' in let p = String.sub s 0 i in let x = String.sub s (i + 1) (String.length s - i - 1) in let v = if p <> "<>" then prefix ^ "." ^ p else prefix in if Hashtbl.mem nametab x then Queue.push v (Hashtbl.find nametab x) else begin let stack = Queue.create () in Queue.push v stack; Hashtbl.add nametab x stack end with Not_found -> () end; pom prefix ic in let ic = open_in tfname in let s = input_line ic in let prefix = String.sub s 1 (String.length s - 1) in try pom prefix ic with End_of_file -> close_in ic; prefix in let rec pom prefix ic oc last = let s = String.trim (input_line ic) in let last2 = if Lib.string_begins_with s "Instance " then get_name s (String.length "Instance ") else if Lib.string_begins_with s "Theorem " then get_name s (String.length "Theorem ") else if Lib.string_begins_with s "Lemma " then get_name s (String.length "Lemma ") else if Lib.string_begins_with s "Definition " then get_name s (String.length "Definition ") else if Lib.string_begins_with s "Fact " then get_name s (String.length "Fact ") else if Lib.string_begins_with s "Corollary " then get_name s (String.length "Corollary ") else if Lib.string_begins_with s "Example " then get_name s (String.length "Example ") else if Lib.string_begins_with s "Remark " then get_name s (String.length "Remark ") else if Lib.string_begins_with s "Global Instance " then get_name s (String.length "Global Instance ") else if Lib.string_begins_with s "Program Instance " then get_name s (String.length "Program Instance ") else if Lib.string_begins_with s "Program Definition " then get_name s (String.length "Program Definition ") else if Lib.string_begins_with s "Program Lemma " then get_name s (String.length "Program Lemma ") else if Lib.string_begins_with s "Program Theorem " then get_name s (String.length "Program Theorem ") else if Lib.string_begins_with s "Program Fact " then get_name s (String.length "Program Fact ") else if Lib.string_begins_with s "Program Corollary " then get_name s (String.length "Program Corollary ") else if Lib.string_begins_with s "Global Program Instance " then get_name s (String.length "Global Program Instance ") else if Lib.string_begins_with s "Local Instance " then get_name s (String.length "Local Instance ") else if Lib.string_begins_with s "Local Program Instance " then get_name s (String.length "Local Program Instance ") else if Lib.string_begins_with s "Let " then get_name s (String.length "Let ") else last in begin let s = remove_hammer_hook s in try if Lib.string_begins_with s "Proof." || Lib.string_begins_with s "Proof with " || Lib.string_begins_with s "Proof using " || Lib.string_begins_with s "Proof using." then begin let pref = Queue.pop (Hashtbl.find nametab last2) in let path = pref ^ "." ^ last2 in let i = String.index s '.' in let p = String.sub s 0 (i + 1) in let r = if i + 1 = String.length s then "" else (String.sub s (i + 1) (String.length s - i - 1)) in output_string oc (p ^ " hammer_hook \"" ^ prefix ^ "\" \"" ^ path ^ "\"." ^ r ^ "\n"); print_endline path end else if Lib.string_begins_with s "Proof " then begin let pref = Queue.pop (Hashtbl.find nametab last2) in let path = pref ^ "." ^ last2 in let p = String.sub s 6 (String.length s - 7) in output_string oc ("Proof. hammer_hook \"" ^ prefix ^ "\" \"" ^ path ^ "\". " ^ "exact (" ^ p ^ "). Qed.\n"); print_endline path end else output_string oc (s ^ "\n") with Not_found | Queue.Empty -> output_string oc (s ^ "\n") end; pom prefix ic oc last2 in let gname = (Filename.chop_suffix fname ".v") ^ ".glob" in let cmd1 = "grep \"^F\" " ^ gname and cmd2 = "grep -v \"^R\" " ^ gname ^ " | tail -n+2 | cut -d ' ' -f 3,4" in let tfname = Filename.temp_file "coqnames" ".glob" and ofname = Filename.temp_file "coqnames" ".v" in ignore (Sys.command (cmd1 ^ " > " ^ tfname)); ignore (Sys.command (cmd2 ^ " >> " ^ tfname)); let prefix = create_nametab tfname in Sys.remove tfname; let ic = open_in fname and oc = open_out ofname in output_string oc "From Hammer Require Import Hammer.\n\n"; try pom prefix ic oc "" with End_of_file -> close_in ic; close_out oc; ignore (Sys.command ("mv " ^ ofname ^ " " ^ fname)) let rec process_dir dir = let entries = Sys.readdir dir in Sys.chdir dir; Array.iter begin fun fname -> if Sys.is_directory fname then process_dir fname else if Filename.check_suffix fname ".v" then process_file fname else () end entries; Sys.chdir ".." ;; process_dir "." coqhammer-1.3.3-9.2/eval/tools/fixreqs.ml000066400000000000000000000032111522316141700201010ustar00rootroot00000000000000(* The program to fix "Require" statements in Coq source code *) let remove_trailing_dot s = let len = String.length s in if len > 0 && String.get s (len - 1) = '.' then String.sub s 0 (len - 1) else s let process_file fname = let rec pom ic oc = let rec hlp prefix lst = match lst with | file :: lst2 -> let from = if Sys.file_exists (file ^ ".v") then "From " ^ Sys.argv.(1) ^ " " else "" in output_string oc (from ^ prefix ^ file ^ ".\n"); hlp prefix lst2 | [] -> () in let s = String.trim (input_line ic) in begin let words = Str.split (Str.regexp "[ ]+") (remove_trailing_dot s) in match words with | "Require" :: "Import" :: lst -> hlp "Require Import " lst | "Require" :: "Export" :: lst -> hlp "Require Export " lst | "Require" :: lst -> hlp "Require " lst | _ -> output_string oc (s ^ "\n") end; pom ic oc in let ofname = Filename.temp_file "fixreqs" ".v" in let ic = open_in fname and oc = open_out ofname in try pom ic oc with End_of_file -> close_in ic; close_out oc; ignore (Sys.command ("mv " ^ ofname ^ " " ^ fname)) let rec process_dir dir = let entries = Sys.readdir dir in Sys.chdir dir; Array.iter begin fun fname -> if Sys.is_directory fname then process_dir fname else if Filename.check_suffix fname ".v" then process_file fname else () end entries; Sys.chdir ".." ;; if Array.length Sys.argv <> 2 then prerr_endline "usage: fixreqs prefix" else process_dir "." coqhammer-1.3.3-9.2/eval/tools/fixreqs.sh000077500000000000000000000001121522316141700201030ustar00rootroot00000000000000#!/bin/bash DIR=`dirname "$0"` "$DIR/rmcomments.sh" "$DIR/fixreqs" "$1" coqhammer-1.3.3-9.2/eval/tools/mkhooks.sh000077500000000000000000000001061522316141700201000ustar00rootroot00000000000000#!/bin/bash DIR=`dirname "$0"` "$DIR/rmcomments.sh" "$DIR/coqnames" coqhammer-1.3.3-9.2/eval/tools/rmcomments.c000066400000000000000000000013401522316141700204170ustar00rootroot00000000000000 #include int main() { int c; int prev = 0; int nesting = 0; int in_string = 0; while ((c = getchar()) != EOF) { if (prev == '(' && c == '*' && !in_string) { ++nesting; } else if (prev == '*' && c == ')' && !in_string && nesting > 0) { --nesting; prev = getchar(); if (prev == EOF) { break; } continue; } if (nesting == 0 && prev != 0) { putchar(prev); } prev = c; if (c == '"' && nesting == 0) { in_string = 1 - in_string; } } if (nesting == 0 && prev != 0 && prev != EOF) { putchar(prev); } return 0; } coqhammer-1.3.3-9.2/eval/tools/rmcomments.sh000077500000000000000000000002201522316141700206060ustar00rootroot00000000000000#!/bin/bash DIR=`dirname "$0"` for f in `find . -name "*.v" -print`; do cp "$f" "$f.bak" cat "$f.bak" | "$DIR/rmcomments" > "$f" done coqhammer-1.3.3-9.2/eval/tools/stat.ml000066400000000000000000000362031522316141700174020ustar00rootroot00000000000000open Utils;; let reconstr_mode = ref false let comma_rxp = Str.regexp ",";; let pom l s nos fg g2 = (Str.split comma_rxp l, Str.split comma_rxp s, List.sort compare (List.map int_of_string (Str.split comma_rxp nos)), Str.split comma_rxp fg, bool_of_string g2);; let (collabels, sortmode, merge_nos, fixgreed, greed2) = match Array.to_list Sys.argv with | [_; "-r"; l; s; nos; fg; g2] -> reconstr_mode := true; pom l s nos fg g2 | [_; l; s; nos; fg; g2] -> pom l s nos fg g2 | _ -> failwith "Usage: stath (labels) (sorting) (merging) (fixgreed) greed2\nwhere [sorting] can be none, sort, greed and [megring] are nos to merge from back";; let proto_rxp = Str.regexp "protokoll";; let dirents d = let dirh = Unix.opendir d in let goodname s = s <> "." && s <> ".." && (try ignore (Str.search_forward proto_rxp s 0); false with Not_found -> true) in let rec fs acc = try fs (let l = Unix.readdir dirh in if goodname l then l :: acc else acc) with End_of_file -> acc in let ret = fs [] in Unix.closedir dirh; ret ;; let rec rdirents prefix acc d = try let dirh = Unix.opendir (prefix ^ d) in let goodname s = s <> "." && s <> ".." && (try ignore (Str.search_forward proto_rxp s 0); false with Not_found -> true) in let rec fs acc = try fs (let l = Unix.readdir dirh in if goodname l then rdirents (prefix ^ d ^ "/") acc l else acc) with End_of_file -> acc in let ret = fs acc in Unix.closedir dirh; ret with Unix.Unix_error (Unix.ENOTDIR, _, _) -> (prefix ^ d) :: acc ;; let rdirents () = if !reconstr_mode then let l = rdirents "" [] "atp/i/f" in List.map (fun s -> String.sub s 8 (String.length s - 8)) l else let l = rdirents "" [] "i/f" in List.map (fun s -> String.sub s 4 (String.length s - 4)) l ;; let dash_rxp = Str.regexp "-";; let unmerged_atps = Array.of_list (dirents (if !reconstr_mode then "out" else "o"));; let rec replace_nos str_lst = function [] -> str_lst | no :: nos -> match str_lst with [] -> failwith "Merge non-existing fields" | sh :: st -> let pnos = List.map pred nos in if no = 0 then "*" :: replace_nos st pnos else sh :: replace_nos st (pred no :: pnos);; let replace_nos s = String.concat "-" (List.rev (replace_nos (List.rev (Str.split dash_rxp s)) merge_nos));; let merged_atps = Hashtbl.create 100;; let merged_atp_no = ref 0;; let replh = Hashtbl.create 100;; let replnoh = Hashtbl.create 100;; Array.iteri (fun un ua -> let ma = replace_nos ua in Hashtbl.replace replh ua ma; try let mn = Hashtbl.find merged_atps ma in Hashtbl.replace replnoh un mn with Not_found -> Hashtbl.add merged_atps ma !merged_atp_no; Hashtbl.replace replnoh un !merged_atp_no; incr merged_atp_no) unmerged_atps;; let reverse_hash h = let nh = Hashtbl.create (Hashtbl.length h) in Hashtbl.iter (fun a b -> Hashtbl.add nh b a) h; nh;; let atpno = !merged_atp_no;; let no_atp = reverse_hash merged_atps;; let atps = Array.init atpno (Hashtbl.find no_atp);; let fixgreed = List.map (fun i -> try Hashtbl.find merged_atps i with _ -> -1) fixgreed;; let fs = Array.of_list (rdirents ());; let fsno = Array.length fs;; Printf.eprintf "e%!";; let reg1 = Str.regexp ".*\\(SZS status Theorem\\|SZS status Unsatisfiable\\| : Valid (\\|SPASS beiseite: Proof found.\\|^Success \\|^THEOREM PROVED$\\)";; let reg2 = Str.regexp ".*\\(SZS status CounterSatisfiable\\|Non-Theorem\\)";; let reg3 = Str.regexp ".*\\(SZS status Timeout\\|SZS status Unknown\\| : Unknown (\\|SZS status ResourceOut\\|^Failure \\|^SPASS beiseite: Ran out of time. SPASS was killed.$\\)";; let reg4 = Str.regexp ".*\\( [eE]rror\\| HighFailure\\|ExitFailure\\|PARSE ERROR\\)";; let evalf fname = try let inc = open_in fname in let rec ans () = try let l = input_line inc in if Str.string_match reg1 l 0 then 5 else if Str.string_match reg2 l 0 then 4 else if Str.string_match reg3 l 0 then 3 else if Str.string_match reg4 l 0 then 2 else ans () with End_of_file -> close_in inc; 1 in let ret = ans () in close_in inc; ret with _ -> 0 ;; let ans = Array.init atpno (fun atp -> Array.create fsno 0);; for uatpno = 0 to Array.length unmerged_atps - 1 do let uatpn = unmerged_atps.(uatpno) in let matpno = Hashtbl.find replnoh uatpno in let fv = ans.(matpno) in for f = 0 to fsno - 1 do let oret = fv.(f) in if oret = 5 then () else begin let name = (if !reconstr_mode then "out/" else "o/") ^ uatpn ^ "/" ^ fs.(f) in let name = if !reconstr_mode then Filename.chop_extension name ^ ".out" else name in let nret = evalf name in if nret > oret then fv.(f) <- nret end done done;; Printf.eprintf "a%!";; (* Problems per atp *) let pps = Array.init atpno (fun matpno -> Array.fold_left (fun s x -> if x > 0 then s + 1 else s) 0 ans.(matpno));; let yes = Array.init atpno (fun atp -> Array.fold_left (fun o i -> o + (if i = 5 then 1 else 0)) 0 ans.(atp));; let no = Array.init atpno (fun atp -> Array.fold_left (fun o i -> o + (if i = 4 then 1 else 0)) 0 ans.(atp));; let maybe = Array.init atpno (fun atp -> Array.fold_left (fun o i -> o + (if i = 3 then 1 else 0)) 0 ans.(atp));; let error = Array.init atpno (fun atp -> Array.fold_left (fun o i -> o + (if i = 2 then 1 else 0)) 0 ans.(atp));; let anyyes, anyno = ref 0, ref 0;; for f = 0 to fsno - 1 do let canayes, canano = ref false, ref false in for a = 0 to atpno - 1 do if ans.(a).(f) = 5 then canayes := true else if ans.(a).(f) = 4 then canano := true done; (if !canayes then incr anyyes); (if !canano then incr anyno); done;; let addl e l = if List.mem e l then l else e :: l;; let uniq = Array.create atpno 0;; for f = 0 to fsno - 1 do let conf1, conf2 = ref [], ref [] in for atp = 0 to atpno - 1 do if ans.(atp).(f) = 5 then begin let canayes = ref true in for a = 0 to atpno - 1 do if ans.(a).(f) = 4 then (conf1 := addl atp !conf1; conf2 := addl a !conf2) else if a <> atp && ans.(a).(f) = 5 then canayes := false else () done; if !canayes then (uniq.(atp) <- uniq.(atp) + 1; print_endline ("Uniq: " ^ atps.(atp) ^ " : " ^ fs.(f))) end done; if !conf1 <> [] then Printf.printf "Conflict: %i Yes: %s No: %s\n" f (String.concat "," (List.map (fun a -> atps.(a)) !conf1)) (String.concat "," (List.map (fun a -> atps.(a)) !conf2)) done;; let sotac = Array.create atpno 0.;; let counter_sotac = false;; for f = 0 to fsno - 1 do let sum = ref 0 in for atp = 0 to atpno - 1 do if ans.(atp).(f) = 5 || (counter_sotac && ans.(atp).(f) = 4) then incr sum; done; let factor = if !sum = 0 then 0. else 1. /. (float_of_int !sum) in for atp = 0 to atpno - 1 do if ans.(atp).(f) = 5 || (counter_sotac && ans.(atp).(f) = 4) then sotac.(atp) <- sotac.(atp) +. factor done done;; let sotacavg = Array.init atpno (fun i -> if yes.(i) = 0 then 0. else sotac.(i) /. (float_of_int (yes.(i) + no.(i))));; let sum2 a1 a2 = let rec sumi acc n = if n = fsno then acc else sumi (if a1.(n) > 4 || a2.(n) > 4 then 1 + acc else acc) (n + 1) in sumi 0 0;; let sum3 a1 a2 a3 = let rec sumi acc n = if n = fsno then acc else sumi (if a1.(n) > 4 || a2.(n) > 4 || a3.(n) > 4 then 1 + acc else acc) (n + 1) in sumi 0 0;; let suml l = let rec sumi acc n = if n = fsno then acc else sumi (if List.fold_left (fun sofar a -> sofar || a.(n) > 4) false l then 1+acc else acc) (n + 1) in sumi 0 0;; let update1 a a1 = let rec ui n = if n = fsno then () else ( (if a1.(n) > 4 then a.(n) <- 5); ui (n + 1)) in ui 0;; let update2 a a1 a2 = let rec ui n = if n = fsno then () else ( (if a1.(n) > 4 || a2.(n) > 4 then a.(n) <- 5); ui (n + 1)) in ui 0;; let arraymaxes f a = let cm = ref 0 and ci = ref [] in for i = 0 to Array.length a - 1 do let fa = f a.(i) in if fa > !cm then (ci := [i]; cm := fa) else if fa = !cm then ci := i :: !ci done; (!ci, !cm);; let current = Array.create fsno 0;; let sofar = ref 0;; let greed_reset () = Array.fill current 0 (Array.length current) 0; sofar := 0;; let id x = x;; let greed_add1 () = let sums = Array.init atpno (fun i -> sum2 current ans.(i)) in let (is, s) = arraymaxes id sums in if s <= !sofar then raise Exit; let a = try List.hd is with Failure _ -> failwith "empty!!!" in let alts = List.tl is in sofar := s; update1 current ans.(a); ((a, alts), s);; let greed_add2 () = let sums = Array.init (atpno * atpno) (fun i -> let a1 = i / atpno and a2 = i mod atpno in sum3 current ans.(a1) ans.(a2)) in let (is, s) = arraymaxes id sums in if s <= !sofar then raise Exit; let is = List.map (fun i -> (i / atpno, i mod atpno)) is in let ((a1, a2) as a) = try List.hd is with Failure _ -> failwith "empty!!!" in let alts = List.tl is in sofar := s; update2 current ans.(a1) ans.(a2); ((a, alts), s);; let greed_add2m () = let sums = Array.init (atpno * atpno) (fun i -> let a1 = i / atpno and a2 = i mod atpno in sum3 current ans.(a1) ans.(a2)) in let (is, s) = arraymaxes id sums in if s <= !sofar then raise Exit; let is = setify (List.concat (List.map (fun i -> [i / atpno; i mod atpno]) is)) in let sums = Array.of_list (List.map (fun i -> (i, sum2 current ans.(i))) is) in let (is, s) = arraymaxes snd sums in let a = fst (sums.(try List.hd is with Failure _ -> failwith "empty!!!")) in sofar := s; update1 current ans.(a); (a, s);; let greed_del1 curlst = let sums = Array.of_list (List.map (fun i -> (i, suml (List.map (Array.get ans) (List.filter (fun j -> j <> i) curlst)))) curlst) in let (is, s) = arraymaxes snd sums in let a = fst (sums.(try List.hd is with Failure _ -> failwith "empty!!!")) in let nlst = (List.filter (fun j -> j <> a) curlst) in sofar := suml (List.map (Array.get ans) nlst); Array.fill current 0 (Array.length current) 0; List.iter (update1 current) (List.map (Array.get ans) nlst); ((a, nlst), !sofar);; Printf.eprintf "s%!";; let greed = ref [];; greed_reset ();; List.iter (fun i -> if i >= 0 then update1 current ans.(i); sofar := suml (List.map (Array.get ans) (i :: (List.map (fun i -> fst (fst i)) !greed))); greed := ((i, []), !sofar) :: !greed; ) fixgreed;; try while true do greed := (greed_add1 ()) :: !greed done with Exit -> ();; let greedy = Array.of_list (List.rev !greed);; Printf.eprintf "g%!";; let name_comp n i = try List.nth (List.rev (Str.split dash_rxp atps.(i))) n with _ -> "";; let rec interp_sort i = function [] | "-" :: _ -> [Printf.sprintf "%010i" i] | "p" :: t -> atps.(i) :: interp_sort i t (* | "g" :: t -> Printf.sprintf "%010i" (1000000000 - (snd (fst greedy.(i)))) :: interp_sort i t*) | "y" :: t -> Printf.sprintf "%010i" (1000000000 - yes.(i)) :: interp_sort i t | "n" :: t -> Printf.sprintf "%010i" (1000000000 - no.(i)) :: interp_sort i t | "s" :: t -> Printf.sprintf "%09.5f" (1000000.0 -. sotac.(i)) :: interp_sort i t | n :: t -> let n = int_of_string n in name_comp n i :: (interp_sort i t) let sort_atp = Array.init atpno (fun i -> (i, interp_sort i sortmode));; Array.sort (fun a b -> compare (snd a) (snd b)) sort_atp;; let proc a b = if b = 0 then "?" else try Printf.sprintf "%.3f" ((100. *. float_of_int a) /. (float_of_int b)) with _ -> "....";; let oc = open_out "statistics.html";; Printf.fprintf oc "\n\n";; let print_table oc l = os oc ""; List.iter (fun (h, _, _) -> os oc "") l; os oc "\n"; for i = 0 to atpno - 1 do os oc ""; let a = fst (sort_atp.(i)) in List.iter (fun (_, (c, v), _) -> os oc "") l; os oc "\n" done; os oc ""; List.iter (fun (_, _, t) -> os oc "") l; os oc "\n
"; os oc h; os oc "
"; os oc (v a); os oc "
"; os oc t; os oc "
\n" ;; print_table oc [ ("Str", ("", name_comp 2), "any"); ("Predict", ("", name_comp 1), "any"); ("PrArg", ("", name_comp 0), ""); ("Thm%", ("yes", fun a -> proc yes.(a) pps.(a)), proc !anyyes fsno); ("CoS%", ("no", fun a -> proc no.(a) pps.(a)), proc !anyno fsno); ("Uniq", ("time", fun a -> string_of_int uniq.(a)), ""); ("ST⌀", ("time", fun a -> Printf.sprintf "%.3f" sotacavg.(a)), ""); ("STΣ", ("time", fun a -> Printf.sprintf "%.2f" sotac.(a)), ""); ("Thm", ("yes", fun a -> string_of_int yes.(a)), string_of_int !anyyes); ("CoS", ("no", fun a -> string_of_int no.(a)), string_of_int !anyno); ("Maybe", ("maybe", fun a -> string_of_int maybe.(a)), ""); ("Empty", ("timeout", fun a -> string_of_int (pps.(a) - yes.(a) - no.(a) - maybe.(a) - error.(a))),""); ("Err", ("error", fun a -> if error.(a) = 0 then "" else string_of_int error.(a)), ""); ("Found", ("time", fun a -> string_of_int pps.(a)), string_of_int fsno) ];; os oc "\n";; let greed = ref [];; if greed2 then begin greed_reset (); try while true do let (((a1,a2), _), s) = greed_add2 () in greed := s :: (-1) :: !greed; Printf.printf "Greed2: %s, %s\n" (Array.get atps a1) (Array.get atps a2) done with Exit -> () end let greedy2 = Array.of_list (List.rev !greed);; if greed2 then begin greed := []; greed_reset (); greed := snd (greed_add1 ()) :: !greed; try while true do let (_, s) = greed_add2 () in greed := s :: (-1) :: !greed done with Exit -> () end let greedy2a = Array.of_list (List.rev !greed);; if greed2 then begin let nos = ref [] in greed := []; greed_reset (); let (((a1, a2), _), s) = greed_add2 () in greed := s :: (-1) :: !greed; nos := a1 :: a2 :: !nos; try while true do let sum = !sofar in let ((_, nnos), _) = greed_del1 !nos in nos := nnos; let (((a1, a2), _), s) = greed_add2 () in nos := a1 :: a2 :: !nos; if s = sum then raise Exit; greed := s :: !greed done with Exit -> () end let greedym1p2 = Array.of_list (List.rev !greed);; if greed2 then begin greed := []; greed_reset (); try while true do let (a, s) = greed_add2m () in greed := s :: !greed done with Exit -> () end let greedy2m = Array.of_list (List.rev !greed);; Printf.fprintf oc "

Greedy sequence

\n";; try for i = 0 to Array.length greedy - 1 do let ((a, alt), m) = greedy.(i) in let alt5s = String.concat " = " (List.map (Array.get atps) (cut_list [] 3 alt)) in let alts = if alt = [] then "" else if List.length alt > 3 then "= " ^ alt5s ^ " = ... (" ^ (string_of_int (List.length alt)) ^ ")" else "= " ^ alt5s in let g2 = if i < Array.length greedy2 && greedy2.(i) >= 0 then string_of_int greedy2.(i) else "" in let g2a = if i < Array.length greedy2a && greedy2a.(i) >= 0 then string_of_int greedy2a.(i) else "" in let g3 = if i < Array.length greedym1p2 && greedym1p2.(i) >= 0 then string_of_int greedym1p2.(i) else "" in let gm = if i < Array.length greedy2m && greedy2m.(i) >= 0 then string_of_int greedy2m.(i) else "" in Printf.fprintf oc "\n" atps.(a) (proc m fsno) m g2 g2a g3 gm alts; if m = !anyyes then raise Exit else () done with Exit -> ();; os oc "
ProverSum%%SumG+2G1+2G-1+2G+2MAlt
%s%s%i%s%s%s%s%s
\n";; close_out oc;; coqhammer-1.3.3-9.2/eval/tools/utils.ml000066400000000000000000000123261522316141700175670ustar00rootroot00000000000000let runline s = let ic = Unix.open_process_in s in let ret = input_line ic in close_in ic; ret;; let uniq l = let rec uniq2 acc = function x::(y::_ as t) -> uniq2 (if Stdlib.compare x y = 0 then acc else x :: acc) t | [x] -> List.rev (x :: acc) | [] -> List.rev acc in uniq2 [] l;; (*let rec uniq = function (x::(y::_ as t) as l) -> let t' = uniq t in if compare x y = 0 then t' else if t'==t then l else x::t' | l -> l;;*) let setify l = uniq (List.sort compare l);; let file_iter fname fn = let ic = try open_in fname with Sys_error _ -> failwith ("file_iter: "^fname) in let next = ref 0 in let rec suck_lines () = fn !next (input_line ic); incr next; suck_lines () in try suck_lines () with End_of_file -> close_in ic;; let os = output_string;; let rec oiter oc fn sep = function [] -> () | [e] -> fn e | h :: t -> fn h; os oc sep; oiter oc fn sep t;; let cutoff = 5;; let stable_sort cmp a lb rb = let merge src1ofs src1len src2 src2ofs src2len dst dstofs = let src1r = src1ofs + src1len and src2r = src2ofs + src2len in let rec loop i1 s1 i2 s2 d = if cmp s1 s2 <= 0 then begin Array.set dst d s1; let i1 = i1 + 1 in if i1 < src1r then loop i1 (Array.get a i1) i2 s2 (d + 1) else Array.blit src2 i2 dst (d + 1) (src2r - i2) end else begin Array.set dst d s2; let i2 = i2 + 1 in if i2 < src2r then loop i1 s1 i2 (Array.get src2 i2) (d + 1) else Array.blit a i1 dst (d + 1) (src1r - i1) end in loop src1ofs (Array.get a src1ofs) src2ofs (Array.get src2 src2ofs) dstofs; in let isortto srcofs dst dstofs len = for i = 0 to len - 1 do let e = (Array.get a (srcofs + i)) in let j = ref (dstofs + i - 1) in while (!j >= dstofs && cmp (Array.get dst !j) e > 0) do Array.set dst (!j + 1) (Array.get dst !j); decr j; done; Array.set dst (!j + 1) e; done; in let rec sortto srcofs dst dstofs len = if len <= cutoff then isortto srcofs dst dstofs len else begin let l1 = len / 2 in let l2 = len - l1 in sortto (srcofs + l1) dst (dstofs + l1) l2; sortto srcofs a (srcofs + l2) l1; merge (srcofs + l2) l1 dst (dstofs + l1) l2 dst dstofs; end; in let l = rb - lb + 1 in if l <= cutoff then isortto lb a lb l else begin let l1 = l / 2 in let l2 = l - l1 in let t = Array.make l2 (Array.get a lb) in sortto (lb + l1) t 0 l2; sortto lb a (lb + l2) l1; merge (lb + l2) l1 t 0 l2 a lb; end; ;; let pivot a l r = let i = ref l and j = ref (r - 1) and p = snd a.(r) in while !i < !j do while snd a.(!i) >= p && !i < r do incr i done; while snd a.(!j) <= p && !j > l do decr j done; if !i < !j then (let t = a.(!i) in a.(!i) <- a.(!j); a.(!j) <- t) done; if snd a.(!i) < p then (let t = a.(!i) in a.(!i) <- a.(r); a.(r) <- t); !i;; let rec qsort a l r upto = if upto > r - l then stable_sort (fun a b -> compare (snd b) (snd a)) a l r else if upto > 0 && l < r then let p = pivot a l r in qsort a l (p - 1) upto; qsort a (p + 1) r (upto + l - p - 1); else ();; let qsort a upto = qsort a 0 (Array.length a - 1) upto;; exception Bottom of int;; let heapsort compare bound a = let maxson l i = let i31 = i+i+i+1 in let x = ref i31 in if i31+2 < l then begin if compare (Array.get a i31) (Array.get a (i31+1)) < 0 then x := i31+1; if compare (Array.get a !x) (Array.get a (i31+2)) < 0 then x := i31+2; !x end else if i31+1 < l && compare (Array.get a i31) (Array.get a (i31+1)) < 0 then i31+1 else if i31 < l then i31 else raise (Bottom i) in let rec trickledown l i e = let j = maxson l i in if compare (Array.get a j) e > 0 then begin Array.set a i (Array.get a j); trickledown l j e; end else begin Array.set a i e; end; in let rec trickle l i e = try trickledown l i e with Bottom i -> Array.set a i e in let rec bubbledown l i = let j = maxson l i in Array.set a i (Array.get a j); bubbledown l j in let bubble l i = try bubbledown l i with Bottom i -> i in let rec trickleup i e = let father = (i - 1) / 3 in assert (i <> father); if compare (Array.get a father) e < 0 then begin Array.set a i (Array.get a father); if father > 0 then trickleup father e else Array.set a 0 e; end else begin Array.set a i e; end; in let l = Array.length a in for i = (l + 1) / 3 - 1 downto 0 do trickle l i (Array.get a i); done; for i = l - 1 downto max 2 (l - bound) do let e = (Array.get a i) in Array.set a i (Array.get a 0); trickleup (bubble i 0) e; done; if l > 1 then (let e = (Array.get a 1) in Array.set a 1 (Array.get a 0); Array.set a 0 e); ;; let rec cut_list acc n = function [] -> List.rev acc | h :: t -> if n = 0 then List.rev acc else cut_list (h :: acc) (n - 1) t;; let list_to_hash exp l = let h = Hashtbl.create exp in List.iter (fun e -> Hashtbl.add h e ()) l; h ;; (* Generate the (inclusive) sequence [l, .., u]. *) let rec fromto l u = if l > u then [] else l :: fromto (l+1) u let string_begins_with s1 s2 = try String.sub s1 0 (String.length s2) = s2 with _ -> false coqhammer-1.3.3-9.2/examples/000077500000000000000000000000001522316141700156205ustar00rootroot00000000000000coqhammer-1.3.3-9.2/examples/euclidean_division.v000066400000000000000000000117401522316141700216470ustar00rootroot00000000000000(* This file demonstrates the use of the `hammer` tactic to find lemmas about real number in the standard library. All tactics with the `use:` option have been obtained by invoking `hammer`. *) (* From Hammer Require Import Hammer. *) From Hammer Require Import Tactics. From Stdlib Require Import Reals. From Stdlib Require Import Lra. Local Open Scope Z_scope. Local Open Scope R_scope. Lemma euclidean_division : forall x y:R, y <> 0 -> exists k : Z, (exists r : R, x = IZR k * y + r /\ 0 <= r < Rabs y). Proof. unfold not; intros x y H. assert (H0: y > 0 \/ y <= 0). { hecrush use: @Rtotal_order unfold: Rle. } destruct H0 as [H0|H0]. - pose (k := (up (x / y) - 1)%Z). exists k. exists (x - IZR k * y). assert (HH: IZR k = IZR (up (x / y)) - 1). { assert (IZR k = IZR (up (x / y)) - IZR 1%Z). { qauto use: @Z_R_minus. } sauto. } rewrite HH; clear HH. clear k. split. + qauto use: RIneq.Rplus_minus. + assert (HH: x - (IZR (up (x / y)) - 1) * y = x - IZR (up (x / y)) * y + y) by lra. rewrite HH; clear HH. split. * assert (IZR (up (x / y)) * y <= y + x). { assert (IZR (up (x / y)) <= 1 + (x / y)). { generalize (archimed (x / y)); sintuition. assert (IZR (up (x / y)) - (x / y) + (x / y) <= 1 + (x / y)). { qauto use: @Rplus_le_compat_r. } scongruence use: Rplus_minus_r, Rplus_minus_swap. } assert (IZR (up (x / y)) * y <= (1 + x / y) * y) by sauto. assert (IZR (up (x / y)) * y <= y + ((x / y) * y)). { qauto use: @Rmult_1_l, @Rmult_plus_distr_r. } hfcrush use: @Rmult_1_r, @Rmult_assoc, @Rinv_l_sym unfold: Rdiv. } lra. * assert (IZR (up (x / y)) * y > x). { assert (IZR (up (x / y)) > x / y). { hauto use: @archimed. } assert (IZR (up (x / y)) * y > (x / y) * y). { hauto use: @Rmult_gt_compat_r. } hfcrush use: @Rmult_1_r, @Rmult_assoc, @Rmult_comm, @Rinv_r_sym unfold: Rdiv. } assert (HH: Rabs y = y). { (* Unset Hammer CVC4. hammer. *) (* If you get an unreconstructible proof, it might help to disable the prover which found it. *) hauto ered: off use: @Rlt_asym unfold: Rabs, Rgt. } rewrite HH; clear HH. lra. - pose (k := (1 - up (x / -y))%Z). exists k. exists (x - IZR k * y). assert (HH: IZR k = 1 - IZR (up (x / -y))). { assert (IZR k = IZR 1 - IZR (up (x / -y))). { hauto ered: off use: @Z_R_minus unfold: Rminus, BinIntDef.Z.sub, Rdiv. } sauto. } rewrite HH; clear HH. clear k. split. + qauto use: @Rplus_minus. + assert (HH: x - (1 - IZR (up (x / - y))) * y = x - y + IZR (up (x / -y)) * y) by lra. rewrite HH; clear HH. split. * assert (IZR (up (x / -y)) * y >= y - x). { assert (IZR (up (x / -y)) <= 1 + (x / -y)). { generalize (archimed (x / -y)); sintuition. assert (IZR (up (x / -y)) - (x / -y) + (x / -y) <= 1 + (x / -y)). { hauto use: @Rplus_comm, @Rplus_le_compat_l. } hcrush use: @Rplus_opp_l, @Rplus_assoc, @Rplus_0_r unfold: Rminus, Rmax. } assert (y < 0) by sauto. assert (IZR (up (x / - y)) * y >= y + (x / - y) * y). { assert (IZR (up (x / - y)) * (-y) <= (1 + (x / - y)) * (-y)). { hauto use: @Ropp_0_ge_le_contravar, @Rmult_le_compat_r, @RIneq.Rle_ge. } assert (HH: IZR (up (x / - y)) * (-y) = - (IZR (up (x / - y)) * y)) by lra. rewrite HH in *; clear HH. assert (HH: (1 + (x / - y)) * (-y) = - (y + (x / -y) * y)) by lra. rewrite HH in *; clear HH. hfcrush use: @Rle_ge, @Ropp_le_cancel unfold: Rle, Rge, Rgt. } assert (HH: y + x / - y * y = y - x). { assert (x / - y * - y = x). { assert (HH1: - y > 0) by lra. assert (HH2: forall u, u <> 0 -> (x / u) * u = x). { hfcrush use: @Rinv_l_sym, @Rmult_1_r, @Rmult_assoc unfold: Rdiv. } (* Unset Hammer CVC4. hammer. *) qauto use: @Rgt_not_eq. } scongruence use: Ropp_mult_distr_r, Ropp_involutive unfold: Rminus. } rewrite HH in *; clear HH. sauto. } lra. * assert (HH: Rabs y = -y). { qauto use: @Rabs_left1. } rewrite HH; clear HH. assert (IZR (up (x / -y)) * y < -x). { assert (IZR (up (x / -y)) > x / -y). { hfcrush use: @archimed. } assert (IZR (up (x / -y)) * -y > (x / -y) * -y). { hauto ered: off use: @Rlt_irrefl, @Rmult_gt_compat_r, @Rabs_pos, @Rabs_pos_lt, @Rle_lt_trans unfold: Rabs, Rle, Rgt. } assert (HH: x / - y * - y = x). { assert (- y <> 0). { hauto use: @Rplus_0_r, @Rplus_opp_r. } assert (forall u, u <> 0 -> (x / u) * u = x). { hfcrush use: @Rmult_1_r, @Rinv_l_sym, @Rmult_assoc unfold: Rdiv. } sauto. } rewrite HH in *; clear HH. lra. } lra. Qed. coqhammer-1.3.3-9.2/examples/hammer_tests.v000066400000000000000000000211331522316141700205020ustar00rootroot00000000000000(* This file showcases hammer usage. Most of the problems here are simple modifications of lemmas present in the standard library (e.g. by changing the order of quantifiers or premises, duplicating some premises, changing function argument order, changing the conclusion to an equivalent one, etc) or a combination of a few lemmas. The calls to the "hammer" tactic are left here only for illustrative purposes. Because the success of the hammer is not guaranteed to be reproducible, in the final scripts "hammer" should be replaced with an appropriate reconstruction tactic. *) From Hammer Require Import Hammer. (*********************************************************************************************) (* Lemma lem_false : False. Proof. hammer. Qed.*) (* Lemma lem_classic : forall P : Prop, P \/ ~P. Proof. hammer. Qed.*) From Stdlib Require Import Arith. (* disable the preliminary sauto tactic *) Set Hammer SAutoLimit 0. Lemma lem_1 : le 1 2. hammer. Restart. scongruence use: Nat.lt_0_2 unfold: lt. Qed. Lemma lem_2 : forall n : nat, Nat.Odd n \/ Nat.Odd (n + 1). hammer. Restart. hauto lq: on use: Nat.Even_or_Odd, Nat.add_1_r, Nat.Odd_succ. Qed. Lemma lem_2_1 : forall n : nat, Nat.Even n \/ Nat.Even (n + 1). hammer. Restart. hauto lq: on use: Nat.add_1_r, Nat.Even_or_Odd, Nat.Even_succ. Qed. Lemma lem_3 : le 2 3. hammer. Restart. srun (eauto) use: Nat.le_succ_diag_r unfold: Init.Nat.two. Qed. Lemma lem_4 : le 3 10. hammer. Restart. sfirstorder use: Nat.nle_succ_0, Nat.le_gt_cases, Nat.lt_succ_r, Nat.succ_le_mono, Nat.log2_up_2 unfold: Init.Nat.two. Qed. Lemma mult_1 : forall m n k : nat, m * n + k = k + n * m. Proof. hammer. Restart. scongruence use: Nat.mul_comm, Nat.add_comm. Qed. Lemma lem_rew : forall m n : nat, 1 + n + m + 1 = m + 2 + n. Proof. hammer. Restart. strivial use: Nat.add_comm, Nat.add_1_r, Nat.add_shuffle1, Nat.add_assoc. Qed. Lemma lem_pow : forall n : nat, 3 * 3 ^ n = 3 ^ (n + 1). Proof. hammer. Restart. qauto use: Nat.pow_succ_r, Nat.le_0_l, Nat.add_1_r. Qed. Require Stdlib.Reals.RIneq. Require Stdlib.Reals.Raxioms. Require Stdlib.Reals.Rtrigo1. Lemma cos_decreasing_1 : forall y x : Rdefinitions.R, Rdefinitions.Rlt x y -> Rdefinitions.Rle x Rtrigo1.PI -> Rdefinitions.Rge y Rdefinitions.R0 -> Rdefinitions.Rle y Rtrigo1.PI -> Rdefinitions.Rge x Rdefinitions.R0 -> Rdefinitions.Rlt (Rtrigo_def.cos y) (Rtrigo_def.cos x). Proof. (* hammer. Restart. *) hauto using (@Reals.Rtrigo1.cos_decreasing_1, @Reals.RIneq.Rge_le). Qed. From Stdlib Require ZArith.BinInt. (* Vampire finds a proof based on Zmax_spec whose case analysis cannot be reconstructed intuitionistically. If you get an unreconstructible proof, it might help to disable the prover which found it. *) Unset Hammer Vampire. Lemma max_lub : forall m p k n : BinNums.Z, BinInt.Z.ge p m -> BinInt.Z.le n p -> BinInt.Z.le (BinInt.Z.max n m) p. Proof. hammer. Restart. srun (eauto) use: BinInt.Z.ge_le, BinInt.Z.max_lub. Qed. Set Hammer Vampire. From Stdlib Require Reals. Lemma lem_iso : forall x1 y1 x2 y2 theta : Rdefinitions.R, Rgeom.dist_euc x1 y1 x2 y2 = Rgeom.dist_euc (Rgeom.xr x1 y1 theta) (Rgeom.yr x1 y1 theta) (Rgeom.xr x2 y2 theta) (Rgeom.yr x2 y2 theta). Proof. hammer. Restart. scongruence use: Rgeom.isometric_rotation. Qed. From Stdlib Require Import List. Lemma lem_lst : forall {A} (x : A) l1 l2 (P : A -> Prop), In x (l1 ++ l2) -> (forall y, In y l1 -> P y) -> (forall y, In y l2 -> P y) -> P x. Proof. hammer. Restart. qauto use: in_app_iff. (* `firstorder with datatypes' does not work *) Qed. Lemma lem_lst2 : forall {A} (y1 y2 y3 : A) l l' z, In z l \/ In z l' -> In z (y1 :: y2 :: l ++ y3 :: l'). Proof. hammer. Restart. hauto lq: on use: in_app_iff, in_or_app, not_in_cons, in_cons, Add_in unfold: app. (* `firstorder with datatypes' does not work *) Qed. Lemma lem_lst3 : forall {A} (l : list A), length (tl l) <= length l. Proof. hammer. Restart. qauto use: le_S, Nat.le_0_l, le_n unfold: tl, length. Qed. From Stdlib Require NArith.Ndec. Lemma Nleb_alt : forall b a c : BinNums.N, Ndec.Nleb b c = BinNat.N.leb b c /\ Ndec.Nleb a b = BinNat.N.leb a b. Proof. hammer. Restart. srun (eauto) use: Ndec.Nleb_alt. Qed. From Stdlib Require NArith.BinNat. Lemma setbit_iff : forall m a n : BinNums.N, n = m \/ true = BinNat.N.testbit a m <-> BinNat.N.testbit (BinNat.N.setbit a n) m = true. Proof. hammer. Restart. hfcrush use: BinNat.N.setbit_iff. Qed. Lemma in_int_p_Sq : forall r p q a : nat, a >= 0 -> Between.in_int p (S q) r -> Between.in_int p q r \/ r = q \/ a = 0. Proof. hammer. Restart. hauto lq: on use: in_int_p_Sq. Qed. From Stdlib Require Reals.Rminmax. Lemma min_spec_1 : forall n m : Rdefinitions.R, (Rdefinitions.Rle m n /\ Rbasic_fun.Rmin m m = m) \/ (Rdefinitions.Rlt n m /\ Rbasic_fun.Rmin m n = n). Proof. hammer. Restart. hauto use: RIneq.Rnot_le_lt unfold: Rbasic_fun.Rmin. Qed. Lemma min_spec_2 : forall n m : Rdefinitions.R, (Rdefinitions.Rle m n /\ Rbasic_fun.Rmin m n = m) \/ (Rdefinitions.Rlt n m /\ Rbasic_fun.Rmin m n = n). Proof. hammer. Restart. hauto use: RIneq.Rnot_le_lt unfold: Rbasic_fun.Rmin. Qed. Lemma incl_app : forall (A : Type) (n l m : list A), List.incl l n /\ List.incl m n -> List.incl (l ++ m) n. Proof. hammer. Restart. strivial use: incl_app. Qed. From Stdlib Require Reals.Rpower. Lemma exp_Ropp : forall x y : Rdefinitions.R, Rdefinitions.Rinv (Rtrigo_def.exp x) = Rtrigo_def.exp (Rdefinitions.Ropp x). Proof. hammer. Restart. srun (eauto) use: Rpower.exp_Ropp. Qed. Lemma lem_lst_1 : forall (A : Type) (l l' : list A), List.NoDup (l ++ l') -> List.NoDup l. Proof. (* The hammer can't do induction. If induction is necessary to carry out the proof, then one needs to start the induction manually. *) induction l'. - hammer. Undo. scongruence use: app_nil_end. - hammer. Undo. srun (eauto) use: NoDup_remove_1. Qed. Lemma NoDup_remove_2 : forall (A : Type) (a : A) (l' l : list A), List.NoDup (l ++ a :: l') -> ~ List.In a (l ++ l') /\ List.NoDup (l ++ l') /\ List.NoDup l. Proof. hammer. Restart. strivial use: lem_lst_1, NoDup_remove. Qed. Lemma leb_compare2 : forall m n : nat, PeanoNat.Nat.leb n m = true <-> (PeanoNat.Nat.compare n m = Lt \/ PeanoNat.Nat.compare n m = Eq). Proof. (* hammer. Restart. *) (* Sometimes the tactics cannot reconstruct the goal, but the returned dependencies may still be used to create the proof semi-manually. *) assert (forall c : Datatypes.comparison, c = Eq \/ c = Lt \/ c = Gt) by sauto inv: Datatypes.comparison. hauto erew: off use: Compare_dec.leb_compare. Qed. Lemma leb_1 : forall m n : nat, PeanoNat.Nat.leb m n = true <-> m <= n. Proof. hammer. Restart. srun (eauto) use: Nat.leb_le, Nat.leb_nle, leb_correct, leb_complete. Qed. Lemma leb_2 : forall m n : nat, PeanoNat.Nat.leb m n = false <-> m > n. Proof. hammer. Restart. srun (eauto) use: leb_iff_conv, leb_correct_conv unfold: gt. Qed. Lemma incl_appl_1 : forall (A : Type) (l m n : list A), List.incl l n -> List.incl l (n ++ m) /\ List.incl l (m ++ n) /\ List.incl l (l ++ l). Proof. hammer. Restart. strivial use: incl_appl, incl_refl, incl_appr. Qed. Lemma in_int_lt2 : forall p q r : nat, Between.in_int p q r -> q >= p /\ r >= p /\ r <= q. Proof. hammer. Restart. sfirstorder use: Nat.lt_le_incl, in_int_lt unfold: ge, in_int. Qed. Lemma nat_compare_eq : forall n m : nat, PeanoNat.Nat.compare n m = Eq <-> n = m. Proof. hammer. Restart. srun (eauto) use: Nat.compare_eq_iff. Qed. Lemma Forall_1 : forall (A : Type) (P : A -> Prop) (a : A), forall (l l' : list A), List.Forall P l /\ List.Forall P l' /\ P a -> List.Forall P (l ++ a :: l'). Proof. induction l. - hammer. Undo. strivial use: app_nil_l, Forall_cons. - (* hammer. Undo. *) sauto use: Forall_cons. Restart. induction l; qsimpl. Qed. (* Neither the base case nor the inductive step may be solved using 'firstorder with datatypes'. *) Lemma Forall_impl : forall (A : Type) (P : A -> Prop), forall l : list A, List.Forall P l -> List.Forall P (l ++ l). Proof. induction l. - hammer. Undo. srun (eauto) use: app_nil_r. - hammer. Undo. qauto use: Forall_inv, Forall_inv_tail, Forall_1. Qed. Lemma minus_neq_O : forall n i:nat, (i < n) -> (n - i) <> 0. Proof. hammer. Undo. srun (eauto) use: Nat.sub_gt. Qed. coqhammer-1.3.3-9.2/examples/sqrt2_irrational.v000066400000000000000000000047261522316141700213170ustar00rootroot00000000000000(* This file contains a proof of the fact that the square root of 2 is irrational. *) (* From Hammer Require Import Hammer. *) From Hammer Require Import Tactics. From Stdlib Require Import Reals. From Stdlib Require Import Arith. From Stdlib Require Import Wf_nat. From Stdlib Require Import Lia. Lemma lem_0 : forall n m, n <> 0 -> m * m = 2 * n * n -> m < 2 * n. Proof. intros n m H H0. destruct (lt_dec m (2 * n)) as [|H1]; try strivial. exfalso. assert (m >= 2 * n) by lia. clear H1. assert (m * m >= 2 * n * (2 * n)). { assert (m * m >= 2 * n * m). { hauto use: @Nat.le_0_l, @Nat.mul_le_mono_nonneg_r unfold: ge. } assert (2 * n * m >= 2 * n * (2 * n)). { hauto use: @Nat.le_0_l, @Nat.mul_le_mono_nonneg_l unfold: ge. } eauto with arith. } sauto. Qed. Lemma lem_main : forall n m, n * n = 2 * m * m -> m = 0. Proof. intro n; pattern n; apply lt_wf_ind; clear n. intros n H m H0. destruct (Nat.eq_dec n 0) as [H1|H1]; subst. - sauto. - destruct (Nat.Even_or_Odd n) as [[k H2]|[k H2]]; subst. + assert (2 * k * k = m * m) by lia. assert (m < 2 * k). { qauto use: @Nat.mul_0_r, @lem_0. } sauto. + sauto. Qed. Theorem thm_irrational : forall (p q : nat), q <> 0 -> sqrt 2 <> (INR p / INR q)%R. Proof. unfold not. intros p q H H0. assert (2 * q * q = p * p). { assert (((sqrt 2) ^ 2)%R = 2%R). { hauto use: @Rsqr_sqrt, @Rlt_R0_R2, @Rsqr_pow2 unfold: Rle. } assert (((INR p / INR q) ^ 2)%R = ((INR p / INR q) * (INR p / INR q))%R). { qauto use: @Rsqr_pow2 unfold: Rsqr. } assert (((INR p / INR q) * (INR p / INR q))%R = ((INR p * INR p) / (INR q * INR q))%R). { hauto use: @Rsqr_div', @not_0_INR. } assert (HH: 2%R = ((INR p * INR p) / (INR q * INR q))%R) by sauto. assert (INR q <> 0%R). { qauto use: @INR_not_0, @INR_eq. } assert (HH2: (2 * INR q * INR q)%R = (INR p * INR p)%R). { rewrite HH; rewrite Rmult_assoc. hfcrush use: @Rinv_l_sym, @Rmult_1_r, @Rmult_integral_contrapositive_currified, @Rmult_assoc unfold: Rsqr, Rdiv. } clear -HH2. assert (forall a b, INR a = INR b -> a = b). { qauto use: @INR_eq. } assert (INR (2 * q * q) = INR (p * p)). { assert (INR (p * p) = (INR p * INR p)%R). { hfcrush use: @mult_INR. } assert (INR (2 * q * q) = (2 * INR q * INR q)%R). { assert (INR (2 * q * q) = (INR 2 * INR q * INR q)%R). { hauto ered: off use: @mult_INR. } sauto. } sauto. } sauto. } sauto use: lem_main. Qed. coqhammer-1.3.3-9.2/examples/tutorial/000077500000000000000000000000001522316141700174635ustar00rootroot00000000000000coqhammer-1.3.3-9.2/examples/tutorial/README.md000066400000000000000000000012261522316141700207430ustar00rootroot00000000000000CoqHammer v1.3 tutorial Tutorial videos are available [here](https://www.youtube.com/watch?v=0c_utk9bVgU&list=PLXXF_svQE_b8ux7fJTL-XX2yjUhYSkYcb). The tutorial files should be read in the following order: 1. [sauto/isort.v](sauto/isort.v) 2. [sauto/isortb.v](sauto/isortb.v) 3. [sauto/itrev.v](sauto/itrev.v) 4. [sauto/order.v](sauto/order.v) 5. [sauto/msort.v](sauto/msort.v) 6. [sauto/imp.v](sauto/imp.v) 7. [sauto/exp.v](sauto/exp.v) 8. [hammer/demo.v](hammer/demo.v) 9. [hammer/gcd.v](hammer/gcd.v) See also a formalisation of various sorting algorithms with `sauto`: [https://github.com/lukaszcz/sortalgs](https://github.com/lukaszcz/sortalgs). coqhammer-1.3.3-9.2/examples/tutorial/hammer/000077500000000000000000000000001522316141700207345ustar00rootroot00000000000000coqhammer-1.3.3-9.2/examples/tutorial/hammer/demo.v000066400000000000000000000112561522316141700220540ustar00rootroot00000000000000(* "hammer" demo *) (* The "hammer" tactic works in three phases: *) (* 1. Machine-learning premise selection. *) (* 2. Translation to automated theorem provers (ATPs). *) (* 3. Proof search in the logic of Coq with the dependencies returned by the ATPs. *) (* CoqHammer uses classical first-order ATPs just to select the right dependencies. The goal must then be re-proven from scratch in the intuitionistic logic of Coq, using the dependencies returned by the ATPs. *) (* The target external tools of CoqHammer are general first-order ATPs, not SMT-solvers. CoqHammer can use some SMT-solvers because in practice they may often be used in the same way as general ATPs. But CoqHammer will never use any of the "modulo theory" features of SMT-solvers. Natural numbers, lists, etc., are not translated in any special way and the SMT-solvers will see them as uninterpreted data types. *) From Hammer Require Import Hammer. (* To use the Hammer module which contains the "hammer" tactic you need to install the full CoqHammer system: opam install coq-hammer Or from source: make && make install *) Hammer_version. Hammer_objects. From Stdlib Require Import Arith. Lemma lem_odd : forall n : nat, Nat.Odd n \/ Nat.Odd (n + 1). Proof. (* hammer. *) hauto lq: on use: Nat.Odd_succ, Nat.Even_or_Odd, Nat.add_1_r. Qed. Lemma lem_even : forall n : nat, Nat.Even n \/ Nat.Even (n + 1). Proof. (* predict 16. *) (* hammer. *) hauto lq: on use: Nat.add_1_r, Nat.Even_or_Odd, Nat.Even_succ. Qed. Lemma lem_pow : forall n : nat, 3 * 3 ^ n = 3 ^ (n + 1). Proof. Fail sauto. (* hammer. *) hauto lq: on use: Nat.pow_succ_r, Nat.le_0_l, Nat.add_1_r. Qed. From Stdlib Require List. Import List.ListNotations. Open Scope list_scope. Lemma lem_incl_concat : forall (A : Type) (l m n : list A), List.incl l n -> List.incl l (n ++ m) /\ List.incl l (m ++ n) /\ List.incl l (l ++ l). Proof. (* hammer. *) strivial use: List.incl_appr, List.incl_refl, List.incl_appl. Qed. Lemma lem_lst_1 : forall (A : Type) (l l' : list A), List.NoDup (l ++ l') -> List.NoDup l. Proof. (* The "hammer" tactic can't do induction. If induction is necessary to carry out the proof, then one needs to start the induction manually. *) induction l'. - (* hammer. *) scongruence use: List.app_nil_r. - (* hammer. *) srun (eauto) use: List.NoDup_remove_1. Qed. From Stdlib Require Import Sorting.Permutation. (* Lemma lem_perm_1 {A} : forall (x y : A) l1 l2 l3, Permutation l1 (y :: l2) -> Permutation (x :: l1 ++ l3) (y :: x :: l2 ++ l3). Proof. hammer. *) Lemma lem_perm_0 {A} : forall (x y : A) l1 l2 l3, Permutation l1 (y :: l2) -> Permutation (x :: l1 ++ l3) (x :: y :: l2 ++ l3). Proof. (* hammer. *) hauto lq: on drew: off use: Permutation_app, List.app_comm_cons, Permutation_refl, perm_skip. Qed. Lemma lem_perm_1 {A} : forall (x y : A) l1 l2 l3, Permutation l1 (y :: l2) -> Permutation (x :: l1 ++ l3) (y :: x :: l2 ++ l3). Proof. (* hammer. *) srun (eauto) use: @lem_perm_0, perm_skip, Permutation_Add, Permutation_trans, Permutation_sym, perm_swap unfold: app. Undo. (* Occasionally, some of the returned dependencies are not necessary. *) srun (eauto) use: @lem_perm_0, Permutation_trans, perm_swap. (* Undo. Set Hammer MinimizationThreshold 0. hammer. *) Qed. (* A general advice: use "hammer" to prove entire lemmas which are stated separately. Using "hammer" to prove subgoals in a larger proof is less effective. One reason is that the machine-learning premise selection can get confused by the presence of unnecessary hypotheses in the context. *) Lemma lem_perm_2 : forall (x : nat) l1 l2 l3, Permutation (x :: l1) l2 -> Permutation (x :: l3 ++ l1) (l3 ++ l2). Proof. (* hammer. *) (* If an ATP returns at least 8 dependencies, then "hammer" tries to automatically minimize the number of dependencies by repeatedly running the ATPs with the returned dependencies as long as some ATP returns fewer dependencies. *) srun (eauto) use: Permutation_app_head, Permutation_trans, Permutation_app_comm, Permutation_cons_app. Qed. Lemma lem_perm_3 : forall (x y : nat) l1 l2 l3, Permutation (x :: l1) l2 -> Permutation (x :: y :: l1 ++ l3) (y :: l2 ++ l3). Proof. (* hammer. *) srun (eauto) use: @lem_perm_1, Permutation_sym. Qed. Lemma lem_perm_4 : forall (x y : nat) l1 l2 l3, Permutation (x :: l1) l2 -> Permutation (x :: y :: l3 ++ l1) (y :: l3 ++ l2). Proof. (* hammer. *) intros. rewrite List.app_comm_cons. pattern (y :: l3 ++ l2). rewrite List.app_comm_cons. apply lem_perm_2; assumption. Qed. (* Lemma lem_classic : forall P : Prop, P \/ ~P. Proof. hammer. Qed.*) coqhammer-1.3.3-9.2/examples/tutorial/hammer/gcd.v000066400000000000000000000054661522316141700216730ustar00rootroot00000000000000From Hammer Require Import Tactics. From Hammer Require Import Hammer. (* for `hammer` *) From Stdlib Require Import Program. From Stdlib Require Import Arith. From Stdlib Require Import Lia. (* Is "d" a common divisor of "a" and "b"? *) Definition is_cd d a b := a mod d = 0 /\ b mod d = 0. (* Is "d" the greatest common divisor of "a" and "b"? *) Definition is_gcd d a b := is_cd d a b /\ forall d', is_cd d' a b -> d' <= d. Lemma lem_gcd_step : forall a b d, b <> 0 -> is_gcd d b (a mod b) -> is_gcd d a b. Proof. unfold is_gcd, is_cd. intros a b d Hb. sintuition. - destruct (Nat.eq_dec d 0) as [Hd|Hd]. + scongruence use: Nat.mod_0_r. + assert (Hc1: exists c1, b = d * c1). { (* hammer. *) strivial use: Nat.Div0.mod_divides. } assert (Hc2: exists c2, a mod b = d * c2). { (* hammer. *) strivial use: Nat.Div0.mod_divides. } assert (Hc3: exists c3, a = b * c3 + a mod b). { (* hammer. *) srun (eauto) use: Nat.div_mod. } clear -Hc1 Hc2 Hc3 Hd. destruct Hc1 as [c1 H1]. destruct Hc2 as [c2 H2]. destruct Hc3 as [c3 H3]. subst. rewrite H2 in H3. subst. assert (H: d * c1 * c3 + d * c2 = (c1 * c3 + c2) * d) by lia. rewrite H. auto using Nat.mod_mul. - enough ((a mod b) mod d' = 0) by auto. destruct (Nat.eq_dec d' 0) as [Hd|Hd]. + scongruence use: Nat.mod_0_r. + assert (Hc1: exists c1, b = d' * c1) by hauto use: Nat.Div0.mod_divides. assert (Hc2: exists c2, a = d' * c2) by hauto use: Nat.Div0.mod_divides. assert (Hc3: exists c3, a = b * c3 + a mod b). { exists (a / b); auto using Nat.div_mod. } clear -Hc1 Hc2 Hc3 Hd Hb. destruct Hc1 as [c1 H1]. destruct Hc2 as [c2 H2]. destruct Hc3 as [c3 H3]. subst. (* hammer. *) clear - Hb Hd. (* Stdlib.Arith.PeanoNat.Nat.mod_mul, Stdlib.Arith.PeanoNat.Nat.mul_mod_distr_l, Stdlib.Arith.PeanoNat.Nat.mul_comm *) rewrite Nat.mul_mod_distr_l; [| lia | lia ]. rewrite Nat.mul_comm. apply Nat.mod_mul; assumption. Qed. Program Fixpoint gcd (a b : nat) {measure b} : {d : nat | a + b > 0 -> is_gcd d a b} := match b with | 0 => a | _ => gcd b (a mod b) end. Next Obligation. unfold is_gcd, is_cd. sintuition. - (* hammer. *) sfirstorder use: Nat.mod_same. - (* hammer. *) (* time sauto. *) (* Set Hammer SAutoLimit 0. hammer. *) sfirstorder use: Nat.mod_0_l. - (* hammer. *) qauto use: Nat.add_pos_cases, Nat.le_gt_cases, Nat.mod_small, Nat.neq_0_lt_0. Qed. Next Obligation. (* hammer. *) srun (eauto) use: Nat.mod_upper_bound. Qed. Next Obligation. simpl_sigma. (* hammer. *) apply lem_gcd_step; [ lia | apply i; lia ]. Qed. Check gcd. Compute ` (gcd 2 3). Compute ` (gcd 5 15). Compute ` (gcd 20 15). Compute ` (gcd 2424 1542). coqhammer-1.3.3-9.2/examples/tutorial/sauto/000077500000000000000000000000001522316141700206165ustar00rootroot00000000000000coqhammer-1.3.3-9.2/examples/tutorial/sauto/exp.v000066400000000000000000000103371522316141700216050ustar00rootroot00000000000000(* Dependently typed expressions *) From Hammer Require Import Tactics. From Stdlib Require Import Program.Equality. (* for "depind" and "depelim" *) From Stdlib Require Import Arith. From Stdlib Require Import String. Inductive type := Nat | Bool | Prod (ty1 ty2 : type). Fixpoint tyeval (ty : type) : Type := match ty with | Nat => nat | Bool => bool | Prod ty1 ty2 => tyeval ty1 * tyeval ty2 end. Inductive expr : type -> Type := | Var : string -> expr Nat | Plus : expr Nat -> expr Nat -> expr Nat | Equal : expr Nat -> expr Nat -> expr Bool | Pair : forall {A B}, expr A -> expr B -> expr (Prod A B) | Fst : forall {A B}, expr (Prod A B) -> expr A | Snd : forall {A B}, expr (Prod A B) -> expr B | Const : forall A, tyeval A -> expr A | Ite : forall {A}, expr Bool -> expr A -> expr A -> expr A. Definition store := string -> nat. Fixpoint eval {A} (s : store) (e : expr A) : tyeval A := match e with | Var n => s n | Plus e1 e2 => eval s e1 + eval s e2 | Equal e1 e2 => eval s e1 =? eval s e2 | Pair e1 e2 => (eval s e1, eval s e2) | Fst e => fst (eval s e) | Snd e => snd (eval s e) | Const _ c => c | Ite b e1 e2 => if eval s b then eval s e1 else eval s e2 end. Definition simp_plus (e1 e2 : expr Nat) := match e1, e2 with | Const Nat n1, Const Nat n2 => Const Nat (n1 + n2) | _, Const Nat 0 => e1 | Const Nat 0, _ => e2 | _, _ => Plus e1 e2 end. Lemma lem_plus : forall s e1 e2, eval s (simp_plus e1 e2) = eval s e1 + eval s e2. Proof. time (depind e1; depelim e2; sauto). (* Undo. time (depind e1; depelim e2; sauto l: on). *) Qed. Lemma lem_plus' : forall s e1 e2, eval s (simp_plus e1 e2) = eval s e1 + eval s e2. Proof. Fail depind e1; sauto. time (depind e1; sauto dep: on). (* "dep: on" instructs "sauto" to use the "depelim" tactic for inversion. This may be slower and it will make your proof depend on axioms (equivalent to Uniqueness of Identity Proofs). *) Qed. Hint Rewrite lem_plus : simp_db. Definition simp_equal (e1 e2 : expr Nat) := match e1, e2 with | Const Nat n1, Const Nat n2 => Const Bool (n1 =? n2) | _, _ => Equal e1 e2 end. Lemma lem_equal : forall s e1 e2, eval s (simp_equal e1 e2) = (eval s e1 =? eval s e2). Proof. Fail depind e1; sauto. time (depind e1; sauto dep: on). Undo. time (depind e1; depelim e2; sauto). Qed. Hint Rewrite lem_equal : simp_db. Definition unpair_type (T : type) := option (match T with Prod A B => expr A * expr B | _ => unit end). Definition unpair {A B : type} (e : expr (Prod A B)) : option (expr A * expr B) := match e in expr T return unpair_type T with | Pair e1 e2 => Some (e1, e2) | _ => None end. Definition simp_fst {A B : type} (e : expr (Prod A B)) : expr A := match unpair e with | Some (e1, e2) => e1 | None => Fst e end. Lemma lem_fst {A B} : forall s (e : expr (Prod A B)), eval s (simp_fst e) = fst (eval s e). Proof. depind e; sauto. Qed. Hint Rewrite @lem_fst : simp_db. Definition simp_snd {A B : type} (e : expr (Prod A B)) : expr B := match unpair e with | Some (e1, e2) => e2 | None => Snd e end. Lemma lem_snd {A B} : forall s (e : expr (Prod A B)), eval s (simp_snd e) = snd (eval s e). Proof. depind e; sauto. Qed. Hint Rewrite @lem_snd : simp_db. Definition simp_ite {A} (e : expr Bool) (e1 e2 : expr A) : expr A := match e with | Const Bool true => e1 | Const Bool false => e2 | _ => Ite e e1 e2 end. Lemma lem_ite {A} : forall s e (e1 e2 : expr A), eval s (simp_ite e e1 e2) = if eval s e then eval s e1 else eval s e2. Proof. depind e; sauto. Qed. Hint Rewrite @lem_ite : simp_db. Fixpoint simp {A} (e : expr A) : expr A := match e with | Var n => Var n | Plus e1 e2 => simp_plus (simp e1) (simp e2) | Equal e1 e2 => simp_equal (simp e1) (simp e2) | Pair e1 e2 => Pair (simp e1) (simp e2) | Fst e => simp_fst (simp e) | Snd e => simp_snd (simp e) | Const t c => Const t c | Ite e e1 e2 => simp_ite (simp e) (simp e1) (simp e2) end. Lemma lem_simp {A} : forall s (e : expr A), eval s (simp e) = eval s e. Proof. time (depind e; sauto use: lem_plus, lem_equal, @lem_fst, @lem_snd, @lem_ite). Undo. time (depind e; sauto db: simp_db). Undo. time (depind e; simpl; autorewrite with simp_db; sauto). Qed. coqhammer-1.3.3-9.2/examples/tutorial/sauto/imp.v000066400000000000000000000314431522316141700215770ustar00rootroot00000000000000(* This file contains a definition of a simple imperative programming language together with its operational semantics and a definition of Hoare logic for it. Most definitions and lemma statements were translated into Coq from Isabelle/HOL statements present in the book: T. Nipkow, G. Klein, Concrete Semantics with Isabelle/HOL. This gives a rough idea of how the automation provided by CoqHammer compares to the automation available in Isabelle/HOL. *) From Hammer Require Import Tactics Reflect. From Stdlib Require Import String. From Stdlib Require Import Arith. From Stdlib Require Import Lia. Open Scope string_scope. Inductive aexpr := | Aval : nat -> aexpr | Avar : string -> aexpr | Aplus : aexpr -> aexpr -> aexpr | Aminus : aexpr -> aexpr -> aexpr. Coercion Aval : nat >-> aexpr. Notation "A +! B" := (Aplus A B) (at level 50). Notation "A -! B" := (Aminus A B) (at level 50). Notation "^ A" := (Avar A) (at level 40). Definition state := string -> nat. Fixpoint aval (s : state) (e : aexpr) := match e with | Aval n => n | Avar x => s x | Aplus x y => aval s x + aval s y | Aminus x y => aval s x - aval s y end. Inductive bexpr := | Bval : bool -> bexpr | Bnot : bexpr -> bexpr | Band : bexpr -> bexpr -> bexpr | Bless : aexpr -> aexpr -> bexpr. Coercion Bval : bool >-> bexpr. Notation "~! A" := (Bnot A) (at level 55). Notation "A &! B" := (Band A B) (at level 55). Notation "A b | Bnot e1 => negb (bval s e1) | Band e1 e2 => bval s e1 && bval s e2 | Bless a1 a2 => (aval s a1 aexpr -> cmd | Seq : cmd -> cmd -> cmd | If : bexpr -> cmd -> cmd -> cmd | While : bexpr -> cmd -> cmd. Notation "A <- B" := (Assign A B) (at level 60). Notation "A ;; B" := (Seq A B) (at level 70). Notation "'If' A 'Then' B 'Else' C" := (If A B C) (at level 65). Notation "'While' A 'Do' B" := (While A B) (at level 65). Definition update (s : state) x v y := if string_dec x y then v else s y. Definition state_subst (s : state) (x : string) (a : aexpr) : state := (update s x (aval s a)). Notation "s [ x := a ]" := (state_subst s x a) (at level 5). (* Big-step operational semantics *) Inductive BigStep : cmd -> state -> state -> Prop := | NopSem : forall s, BigStep Nop s s | AssignSem : forall s x a, BigStep (x <- a) s s[x := a] | SeqSem : forall c1 c2 s1 s2 s3, BigStep c1 s1 s2 -> BigStep c2 s2 s3 -> BigStep (c1 ;; c2) s1 s3 | IfTrue : forall b c1 c2 s s', bval s b -> BigStep c1 s s' -> BigStep (If b Then c1 Else c2) s s' | IfFalse : forall b c1 c2 s s', negb (bval s b) -> BigStep c2 s s' -> BigStep (If b Then c1 Else c2) s s' | WhileFalse : forall b c s, negb (bval s b) -> BigStep (While b Do c) s s | WhileTrue : forall b c s1 s2 s3, bval s1 b -> BigStep c s1 s2 -> BigStep (While b Do c) s2 s3 -> BigStep (While b Do c) s1 s3. Notation "A >> B ==> C" := (BigStep A B C) (at level 80, no associativity). Lemma lem_big_step_deterministic : forall c s s1, c >> s ==> s1 -> forall s2, c >> s ==> s2 -> s1 = s2. Proof. time (induction 1; sauto brefl: on). Undo. time (induction 1; sauto lazy: on brefl: on). Undo. time (induction 1; sauto lazy: on quick: on brefl: on). Qed. (* Program equivalence *) Definition equiv_cmd (c1 c2 : cmd) := forall s s', c1 >> s ==> s' <-> c2 >> s ==> s'. Notation "A ~~ B" := (equiv_cmd A B) (at level 75, no associativity). Lemma lem_sim_refl : forall c, c ~~ c. Proof. sauto. Qed. Lemma lem_sim_sym : forall c c', c ~~ c' -> c' ~~ c. Proof. sauto unfold: equiv_cmd. Qed. Lemma lem_sim_trans : forall c1 c2 c3, c1 ~~ c2 -> c2 ~~ c3 -> c1 ~~ c3. Proof. sauto unfold: equiv_cmd. Qed. Lemma lem_seq_assoc : forall c1 c2 c3, c1;; (c2;; c3) ~~ (c1;; c2);; c3. Proof. time sauto unfold: equiv_cmd. Undo. time sauto lazy: on unfold: equiv_cmd. (* "lazy: on" turns off all eager heuristics *) (* This may sometimes speed up "sauto" noticeably, but sometimes it may prevent "sauto" from solving the goal. *) (* To increase the performance of "sauto" you may need to fiddle with various options. *) (* Things to try which commonly result in speed increase (if "sauto" can still solve the goal): - "lazy: on" ("l: on") - "quick: on" ("q: on") - a combination of various options which typically make "sauto" faster but weaker; this is more conservative than "qauto" which additionally severely decreases the proof cost limit - "lq: on" - an abbreviation for "l: on q: on" - "erew: off" - turn off eager rewriting - "rew: off" - turn off rewriting entirely - "ered: off" - turn off eager reduction with "simpl" - "red: off" - turn off reduction entirely - "ecases: off" - turn off eager case splitting - "cases: -" - turn off case splitting entirely - "einv: off sinv: off" - turn off eager inversion heuristics *) Qed. Lemma lem_triv_if : forall b c, If b Then c Else c ~~ c. Proof. unfold equiv_cmd. intros b c s s'. destruct (bval s b) eqn:?; sauto. Qed. Lemma lem_commute_if : forall b1 b2 c1 c2 c3, If b1 Then (If b2 Then c1 Else c2) Else c3 ~~ If b2 Then (If b1 Then c1 Else c3) Else (If b1 Then c2 Else c3). Proof. unfold equiv_cmd. intros *. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto). Undo. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto inv: BigStep ctrs: BigStep). Undo. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto quick: on inv: BigStep ctrs: BigStep). (* "quick: on" sets various options in a way which typically makes "sauto" weaker but faster. "quato" is "hauto" with "quick: on", a smaller cost limit and a different leaf solver. See https://github.com/lukaszcz/coqhammer#Sauto for details. *) Undo. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto lazy: on inv: BigStep ctrs: BigStep). Undo. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto lazy: on quick: on inv: BigStep ctrs: BigStep). Undo. time (destruct (bval s b1) eqn:?; destruct (bval s b2) eqn:?; sauto lq: on inv: BigStep ctrs: BigStep). (* "lq: on" is an abbreviation for "lazy: on quick: on" *) (* "lazy:" may be abbreviated to "l:" *) (* "quick:" may be abbreviated to "q:" *) Qed. Lemma lem_unfold_while : forall b c, While b Do c ~~ If b Then c;; While b Do c Else Nop. Proof. time sauto unfold: equiv_cmd. Undo. time sauto q: on unfold: equiv_cmd. (* "quick: on" does not result in significant speed increase this time *) Undo. time sauto l: on unfold: equiv_cmd. (* "lazy: on" does *) Qed. Lemma lem_while_cong_aux : forall b c c' s s', While b Do c >> s ==> s' -> c ~~ c' -> While b Do c' >> s ==> s'. Proof. intros *. remember (While b Do c). induction 1; sauto lq: on unfold: equiv_cmd. Qed. Lemma lem_while_cong : forall b c c', c ~~ c' -> While b Do c ~~ While b Do c'. Proof. hauto use: lem_while_cong_aux unfold: equiv_cmd. Qed. (* Small-step operational semantics *) Inductive SmallStep : cmd * state -> cmd * state -> Prop := | AssignSemS : forall x a s, SmallStep (x <- a, s) (Nop, s[x := a]) | SeqSemS1 : forall c s, SmallStep (Nop ;; c, s) (c, s) | SeqSemS2 : forall c1 c2 s c1' s', SmallStep (c1, s) (c1', s') -> SmallStep (c1 ;; c2, s) (c1';; c2, s') | IfTrueS : forall b c1 c2 s, bval s b -> SmallStep (If b Then c1 Else c2, s) (c1, s) | IfFalseS : forall b c1 c2 s, negb (bval s b) -> SmallStep (If b Then c1 Else c2, s) (c2, s) | WhileS : forall b c s, SmallStep (While b Do c, s) (If b Then c;; While b Do c Else Nop, s). Notation "A --> B" := (SmallStep A B) (at level 80, no associativity). From Stdlib Require Import Relations. Definition SmallStepStar := clos_refl_trans (cmd * state) SmallStep. Notation "A -->* B" := (SmallStepStar A B) (at level 80, no associativity). Lemma lem_small_step_deterministic : forall p p1, p --> p1 -> forall p2, p --> p2 -> p1 = p2. Proof. induction 1; sauto lq: on brefl: on. Qed. (* Equivalence between big-step and small-step operational semantics *) Lemma lem_star_seq2 : forall c1 c2 s c1' s', (c1, s) -->* (c1', s') -> (c1;; c2, s) -->* (c1';; c2, s'). Proof. enough (forall p1 p2, p1 -->* p2 -> forall c1 c2 s c1' s', p1 = (c1, s) -> p2 = (c1', s') -> (c1;; c2, s) -->* (c1';; c2, s')). { eauto. } induction 1; sauto lq: on. Qed. Lemma lem_seq_comp : forall c1 c2 s1 s2 s3, (c1, s1) -->* (Nop, s2) -> (c2, s2) -->* (Nop, s3) -> (c1;; c2, s1) -->* (Nop, s3). Proof. intros c1 c2 s1 s2 s3 H1 H2. assert ((c1;; c2, s1) -->* (Nop;; c2, s2)) by sauto use: lem_star_seq2. sauto. Qed. Lemma lem_big_to_small : forall c s s', c >> s ==> s' -> (c, s) -->* (Nop, s'). Proof. intros c s s' H. induction H as [ | | | | | | b c s1 s2 ]. - sauto. - sauto. - sauto use: lem_seq_comp. - sauto. - sauto. - sauto. - assert ((While b Do c, s1) -->* (c;; While b Do c, s1)) by sauto. assert ((c;; While b Do c, s1) -->* (Nop;; While b Do c, s2)) by sauto use: lem_star_seq2. sauto. Qed. Lemma lem_small_to_big_aux : forall p p', p --> p' -> forall c1 s1 c2 s2 s, p = (c1, s1) -> p' = (c2, s2) -> c2 >> s2 ==> s -> c1 >> s1 ==> s. Proof. time (induction 1; sauto). Undo. time (induction 1; sauto l: on). Undo. time (induction 1; sauto lq: on). Qed. Lemma lem_small_to_big_aux_2 : forall p p', p -->* p' -> forall c1 s1 c2 s2 s, p = (c1, s1) -> p' = (c2, s2) -> c2 >> s2 ==> s -> c1 >> s1 ==> s. Proof. induction 1; sauto use: lem_small_to_big_aux. Qed. Lemma lem_small_to_big : forall c s s', (c, s) -->* (Nop, s') -> c >> s ==> s'. Proof. enough (forall p p', p -->* p' -> forall c s s', p = (c, s) -> p' = (Nop, s') -> c >> s ==> s') by eauto. time (induction 1; sauto use: lem_small_to_big_aux_2). Undo. time (induction 1; sauto l: on use: lem_small_to_big_aux_2). (* "l: on" slightly improves performance *) (* Undo. induction 1; sauto q: on use: lem_small_to_big_aux_2. *) (* But "q: on" prevents "sauto" from solving the goal. *) Qed. Corollary cor_big_iff_small : forall c s s', c >> s ==> s' <-> (c, s) -->* (Nop, s'). Proof. sauto use: lem_small_to_big, lem_big_to_small. Qed. (* Hoare triples *) Definition assn := state -> Prop. Definition HoareValid (P : assn) (c : cmd) (Q : assn): Prop := forall s s', c >> s ==> s' -> P s -> Q s'. Notation "|= {{ P }} c {{ Q }}" := (HoareValid P c Q). (* Hoare logic *) Definition entails (P Q : assn) : Prop := forall s, P s -> Q s. Inductive Hoare : assn -> cmd -> assn -> Prop := | Hoare_Nop : forall P, Hoare P Nop P | Hoare_Assign : forall P a x, Hoare (fun s => P s[x := a]) (x <- a) P | Hoare_Seq : forall P Q R c1 c2, Hoare P c1 Q -> Hoare Q c2 R -> Hoare P (c1 ;; c2) R | Hoare_If : forall P Q b c1 c2, Hoare (fun s => P s /\ bval s b) c1 Q -> Hoare (fun s => P s /\ negb (bval s b)) c2 Q -> Hoare P (If b Then c1 Else c2) Q | Hoare_While : forall P b c, Hoare (fun s => P s /\ bval s b) c P -> Hoare P (While b Do c) (fun s => P s /\ negb (bval s b)) | Hoare_conseq: forall P P' Q Q' c, Hoare P c Q -> entails P' P -> entails Q Q' -> Hoare P' c Q'. Notation "|- {{ s | P }} c {{ s' | Q }}" := (Hoare (fun s => P) c (fun s' => Q)). Notation "|- {{ s | P }} c {{ Q }}" := (Hoare (fun s => P) c Q). Notation "|- {{ P }} c {{ s' | Q }}" := (Hoare P c (fun s' => Q)). Notation "|- {{ P }} c {{ Q }}" := (Hoare P c Q). Lemma lem_hoare_strengthen_pre : forall P P' Q c, entails P' P -> |- {{P}} c {{Q}} -> |- {{P'}} c {{Q}}. Proof. sauto unfold: entails. Qed. Lemma lem_hoare_weaken_post : forall P Q Q' c, entails Q Q' -> |- {{P}} c {{Q}} -> |- {{P}} c {{Q'}}. Proof. sauto unfold: entails. Qed. Lemma hoare_assign : forall (P Q : assn) x a, (forall s, P s -> Q s[x := a]) -> |- {{P}} x <- a {{Q}}. Proof. sauto use: lem_hoare_strengthen_pre unfold: entails. Qed. Lemma hoare_while : forall b (P Q: assn) c, |- {{s | P s /\ bval s b}} c {{P}} -> (forall s, P s /\ negb (bval s b) -> Q s) -> |- {{P}} (While b Do c) {{Q}}. Proof. sauto use: lem_hoare_weaken_post unfold: entails. Qed. (* Soundness of Hoare logic *) Theorem thm_hoare_correct : forall P Q c, |- {{P}} c {{Q}} -> |= {{P}} c {{Q}}. Proof. unfold HoareValid. induction 1. - sauto. - sauto. - sauto inv: BigStep. - sauto inv: BigStep. - intros *. remember (While b Do c). induction 1; qauto inv: BigStep. - sauto unfold: entails. Qed. coqhammer-1.3.3-9.2/examples/tutorial/sauto/isort.v000066400000000000000000000111431522316141700221450ustar00rootroot00000000000000(******************************************************************) (* Insertion sort *) From Hammer Require Import Tactics. (* CoqHammer tactics v1.3 or later *) (* Installation: opam repo add coq-released https://coq.inria.fr/opam/released opam install coq-hammer-tactics *) (* Alternatively, download the latest release form https://github.com/lukaszcz/coqhammer, and after unpacking run `make tactics` and `make install-tactics` *) (* Documentation is available at: https://github.com/lukaszcz/coqhammer. *) From Stdlib Require List. Import List.ListNotations. Open Scope list_scope. From Stdlib Require Import Arith. From Stdlib Require Import Lia. Inductive Sorted : list nat -> Prop := | Sorted_0 : Sorted [] | Sorted_1 : forall x, Sorted [x] | Sorted_2 : forall x y l, Sorted (y :: l) -> x <= y -> Sorted (x :: y :: l). (* insert a number into a sorted list preserving the sortedness *) Fixpoint insert (l : list nat) (x : nat) : list nat := match l with | [] => [x] | h :: t => if x <=? h then x :: l else h :: insert t x end. (* insertion sort *) Fixpoint isort (l : list nat) : list nat := match l with | [] => [] | h :: t => insert (isort t) h end. Lemma lem_insert_sorted_hlp : forall l y z, y <= z -> Sorted (y :: l) -> Sorted (y :: insert l z). Proof. intro l. induction l as [|a l IH]. - intros; simpl; auto using Sorted. - intros x y H1 H2. simpl. destruct (Nat.leb_spec y a) as [H|H]. + repeat constructor; auto. inversion H2; auto. + inversion_clear H2. auto using Sorted with arith. Qed. Lemma lem_insert_sorted_hlp' : forall l y z, y <= z -> Sorted (y :: l) -> Sorted (y :: insert l z). Proof. (* "sauto" will *never* try "induction" - one needs to first invoke "induction" manually *) time (induction l; sauto db: arith). (* "db: db1, .., dbn" instructs "sauto" to use the given hint or rewriting databases *) Undo. time (induction l; sauto db: arith inv: Sorted ctrs: Sorted). (* "inv: ind1, .., indn" instructs "sauto" to try inversion (case reasoning) only on elements of the given inductive types *) (* "ctrs: ind1, .., indn" instructs "sauto" to try using constructors of only the given inductive types *) (* "-" stands for an empty list, "*" for a list of all possible inductive types *) (* By default "sauto" tries inversion on elements of and uses constructors of all possible inductive types *) (* I.e. the defaults are: "inv: *" and "ctrs: *" *) Qed. Lemma lem_insert_sorted (l : list nat) (x : nat) : Sorted l -> Sorted (insert l x). Proof. destruct l as [|y l]. - simpl; auto using Sorted. - intro H. simpl. destruct (Nat.leb_spec x y); auto using Sorted, lem_insert_sorted_hlp with arith. Qed. Lemma lem_insert_sorted' (l : list nat) (x : nat) : Sorted l -> Sorted (insert l x). Proof. (* sauto use: lem_insert_sorted_hlp db: arith. *) (* "use: lem1, .., lemn" adds the given lemmas to the context *) (* The default is "use: -" *) (* "sauto" above does not find a proof in reasonable time *) (* Sometimes it is enough to help "sauto" just by providing a few initial steps (particularly when the first step is "destruct" or "inversion") *) time (destruct l; sauto use: lem_insert_sorted_hlp db: arith). Undo. time (destruct l; sauto use: lem_insert_sorted_hlp inv: - ctrs: Sorted db: arith). (* Providing the "inv:" and "ctrs:" options with only the necessary inductive types often noticeably decreases the running time *) (* There is a shorthand for this common use case: "hauto" is "sauto inv: - ctrs: -" *) Qed. Lemma lem_isort_sorted : forall l, Sorted (isort l). Proof. induction l; simpl; auto using Sorted, lem_insert_sorted. Qed. Lemma lem_isort_sorted' : forall l, Sorted (isort l). Proof. induction l; sauto use: lem_insert_sorted. Qed. (* We have proven that the result of "isort" is a sorted list. Now we prove that the result is a permutation of the argument. *) From Stdlib Require Import Sorting.Permutation. Lemma lem_insert_perm : forall l x, Permutation (insert l x) (x :: l). Proof. induction l as [|y ? ?]. - eauto using Permutation. - intro x. simpl. destruct (Nat.leb_spec x y) as [H|H]; eauto using Permutation. Qed. Lemma lem_insert_perm' : forall l x, Permutation (insert l x) (x :: l). Proof. induction l; sauto. Qed. Lemma lem_isort_perm : forall l, Permutation (isort l) l. Proof. induction l; simpl; eauto using Permutation, lem_insert_perm. Qed. Lemma lem_isort_perm' : forall l, Permutation (isort l) l. Proof. induction l; sauto use: lem_insert_perm. Qed. coqhammer-1.3.3-9.2/examples/tutorial/sauto/isortb.v000066400000000000000000000105261522316141700223130ustar00rootroot00000000000000(******************************************************************) (* Insertion sort (boolean version) *) From Hammer Require Import Tactics. From Hammer Require Import Reflect. (* The Reflect module declares "is_true" as a coercion and defines some tactics related to boolean reflection. *) From Stdlib Require List. Import List.ListNotations. Open Scope list_scope. From Stdlib Require Import Arith. From Stdlib Require Import Lia. From Stdlib Require Import Bool. Inductive Sorted : list nat -> Prop := | Sorted_0 : Sorted [] | Sorted_1 : forall x, Sorted [x] | Sorted_2 : forall x y l, Sorted (y :: l) -> x <= y -> Sorted (x :: y :: l). Fixpoint sortedb (l : list nat) : bool := match l with | [] => true | [x] => true | x :: (y :: l') as t => (x <=? y) && sortedb t end. Lemma lem_sortedb_iff_sorted : forall l, sortedb l <-> Sorted l. Proof. induction l; sauto brefl: on. (* The "brefl: on" option enables boolean reflection - automatic conversion of boolean statements (arguments to the "is_true" coercion) into corresponding propositions in Prop. *) Qed. Lemma lem_sortedb_to_sorted_step_by_step : forall l, sortedb l -> Sorted l. Proof. induction l as [| x l IH]. - sauto. - (* sauto. *) simpl. case_split; try strivial. (* "case_split" eliminates one discriminee of a match expression occurring in the goal or in a hypothesis *) breflect. (* "breflect" performs boolean reflection - it implements the "brefl:" option *) (* sauto. *) (* By default "sauto" eagerly eliminates discriminees of all match expressions. This behaviour is controlled by the "ecases:" option. *) (* simpl. case_splitting. *) (* "case_splitting" repeatedly runs "case_split", "subst" and "simpl" - it implements the "cases:" and "ecases:" options *) sauto ecases: off. Undo. sauto cases: -. (* One case specify the inductive types whose elements should be eliminated when they appear as a discriminee of a match expression *) Undo. sauto brefl: on. (* Setting "brefl: on" implies "ecases: off" because eager case splitting is often detrimental in combination with boolean reflection. *) (* Undo. sauto brefl!: on. *) (* Setting "brefl!: on" enables boolean reflection only without affecting other options. *) (* Some options by default affect other options. A primitive version "opt!:" of an option "opt:" never affects any other options. *) Qed. (* insert a number into a sorted list preserving the sortedness *) Fixpoint insert (l : list nat) (x : nat) : list nat := match l with | [] => [x] | h :: t => if x <=? h then x :: l else h :: insert t x end. (* insertion sort *) Fixpoint isort (l : list nat) : list nat := match l with | [] => [] | h :: t => insert (isort t) h end. Lemma lem_insert_sorted_hlp : forall l y z, y <= z -> sortedb (y :: l) -> sortedb (y :: insert l z). Proof. time (induction l; sauto brefl: on db: arith). Undo. (* We do not need inversions in this proof: set "inv: -" or use "hauto" *) time (induction l; sauto brefl: on inv: - ctrs: - db: arith). Qed. Lemma lem_insert_sorted : forall l x, sortedb l -> sortedb (insert l x). Proof. destruct l; hauto brefl: on use: lem_insert_sorted_hlp db: arith. (* "hauto" is "sauto inv: - ctrs: -" *) Qed. Lemma lem_isort_sorted : forall l, sortedb (isort l). Proof. induction l; sauto use: lem_insert_sorted. Qed. Hint Rewrite -> lem_sortedb_iff_sorted : brefl. (* Boolean reflection can be customised by adding rewrite hints to the "brefl" database. *) Lemma lem_insert_sorted_hlp' : forall l y z, y <= z -> sortedb (y :: l) -> sortedb (y :: insert l z). Proof. breflect. induction l; sauto db: arith. Restart. (* induction l; sauto brefl: on db: arith. *) (* Eager case splitting is usually a good idea for non-boolean goals involving inductive types *) induction l; sauto brefl!: on db: arith. (* "brefl!:" enables boolean reflection without affecting "ecases:" *) Qed. Lemma lem_insert_sorted' : forall l x, sortedb l -> sortedb (insert l x). Proof. destruct l; hauto brefl!: on use: lem_insert_sorted_hlp db: arith ctrs: Sorted. Qed. Lemma lem_isort_sorted' : forall l, sortedb (isort l). Proof. induction l; sauto use: lem_insert_sorted. Qed. coqhammer-1.3.3-9.2/examples/tutorial/sauto/itrev.v000066400000000000000000000074141522316141700221440ustar00rootroot00000000000000(* Tail-recursive reverse *) From Hammer Require Import Tactics. From Hammer Require Import Hints. (* The Hints module provides the following rewrite hint databases: shints, slist, sbool, sarith, szarith. *) From Stdlib Require List. Import List.ListNotations. Open Scope list_scope. Fixpoint itrev {A} (l acc : list A) := match l with | [] => acc | h :: t => itrev t (h :: acc) end. Definition rev {A} (l : list A) := itrev l []. Lemma lem_itrev {A} : forall l acc : list A, itrev l acc = itrev l [] ++ acc. Proof. induction l as [| h t IH]. - auto. - intro acc. simpl. rewrite IH. pattern (itrev t [h]). rewrite IH. rewrite <- List.app_assoc. reflexivity. Qed. Lemma lem_itrev' {A} : forall l acc : list A, itrev l acc = itrev l [] ++ acc. Proof. (* induction l; sauto db: slist. *) induction l; ssimpl. (* Simplification tactics in the order of increasing strength and decreasing speed: "simp_hyps", "sintuition", "qsimpl", "ssimpl". *) (* The simplification tactics may change the context in an unpredictable manner and introduce automatically generated hypothesis names. *) rewrite IHl. (* rewrite IHl. *) pattern (itrev l [a]). rewrite IHl. sauto db: slist. (* The "slist" database contains "List.app_assoc" *) (* "sauto" is currently not very good at rewriting - it just tries to apply the "rewrite" tactic *) Restart. induction l as [|x l ?]; simpl. - sauto. - assert (itrev l [x] = itrev l [] ++ [x]) by sauto. sauto db: slist. Qed. Lemma lem_rev_app {A} : forall l1 l2 : list A, rev (l1 ++ l2) = rev l2 ++ rev l1. Proof. unfold rev. induction l1 as [| x l1 IH]; intro l2. - simpl. rewrite List.app_nil_r. reflexivity. - simpl. rewrite lem_itrev. rewrite IH. rewrite <- List.app_assoc. rewrite (lem_itrev l1 [x]). reflexivity. Qed. Lemma lem_rev_app' {A} : forall l1 l2 : list A, rev (l1 ++ l2) = rev l2 ++ rev l1. Proof. induction l1; sauto use: @lem_itrev db: slist unfold: rev. Qed. Lemma lem_rev_rev {A} : forall l : list A, rev (rev l) = l. Proof. unfold rev. induction l as [| x l IH]. - reflexivity. - simpl. rewrite (lem_itrev l [x]). generalize (lem_rev_app (itrev l []) [x]). unfold rev. intro H. rewrite H. rewrite IH. reflexivity. Qed. Lemma lem_rev_rev' {A} : forall l : list A, rev (rev l) = l. Proof. (* induction l; sauto use: @lem_itrev, @lem_rev_app unfold: rev. *) (* induction l; sauto limit: 2000 use: @lem_itrev, @lem_rev_app unfold: rev. *) induction l as [|x l ?]. - reflexivity. - sauto use: (lem_itrev l [x]), (lem_rev_app (itrev l []) [x]) unfold: rev. Qed. Lemma lem_rev_lst {A} : forall l : list A, rev l = List.rev l. Proof. unfold rev. induction l as [|x l IH]. - reflexivity. - simpl. rewrite lem_itrev. rewrite IH. reflexivity. Qed. Lemma lem_rev_lst' {A} : forall l : list A, rev l = List.rev l. Proof. induction l; sauto use: @lem_itrev unfold: rev. Qed. From Stdlib Require Import Sorting.Permutation. Lemma lem_itrev_perm {A} : forall l l' : list A, Permutation (itrev l l') (l ++ l'). Proof. induction l as [| x l IH]; simpl. - eauto using Permutation. - intro l'. enough (Permutation (l ++ (x :: l')) (x :: l ++ l')). { eauto using Permutation. } eauto using Permutation_middle, Permutation_sym. Qed. Lemma lem_itrev_perm' {A} : forall l l' : list A, Permutation (itrev l l') (l ++ l'). Proof. induction l; sauto use: Permutation_middle, Permutation_sym. Qed. Lemma lem_rev_perm {A} : forall l : list A, Permutation (rev l) l. Proof. unfold rev. intro l. rewrite <- List.app_nil_r. apply lem_itrev_perm. Qed. Lemma lem_rev_perm' {A} : forall l : list A, Permutation (rev l) l. Proof. sauto use: @lem_itrev_perm db: slist unfold: rev. Qed. coqhammer-1.3.3-9.2/examples/tutorial/sauto/msort.v000066400000000000000000000171751522316141700221640ustar00rootroot00000000000000From Hammer Require Import Tactics. From Hammer Require Import Reflect. From Stdlib Require List. Open Scope list_scope. Import List.ListNotations. From Stdlib Require Import Arith. From Stdlib Require Import Lia. From Stdlib Require Import Sorting.Permutation. From Stdlib Require Import Program. Class DecTotalOrder (A : Type) := { leb : A -> A -> bool; leb_total_dec : forall x y, {leb x y}+{leb y x}; leb_antisym : forall x y, leb x y -> leb y x -> x = y; leb_trans : forall x y z, leb x y -> leb y z -> leb x z }. Arguments leb {A _}. Arguments leb_total_dec {A _}. Arguments leb_antisym {A _}. Arguments leb_trans {A _}. Instance dto_nat : DecTotalOrder nat. Proof. apply Build_DecTotalOrder with (leb := Nat.leb); induction x; sauto. Defined. Inductive Sorted {A} {dto : DecTotalOrder A} : list A -> Prop := | Sorted_0 : Sorted [] | Sorted_1 : forall x, Sorted [x] | Sorted_2 : forall x y l, Sorted (y :: l) -> leb x y -> Sorted (x :: y :: l). Lemma lem_sorted_tail {A} {dto : DecTotalOrder A} : forall l x, Sorted (x :: l) -> Sorted l. Proof. sauto. Qed. (* "LeLst x l" holds if "x" is smaller or equal to all elements in "l" *) Definition LeLst {A} {dto : DecTotalOrder A} (x : A) := List.Forall (leb x). Lemma lem_lelst_trans {A} {dto : DecTotalOrder A} : forall l x y, LeLst y l -> leb x y -> LeLst x l. Proof. induction 1; sauto. Qed. Lemma lem_lelst_sorted {A} {dto : DecTotalOrder A} : forall l x, Sorted (x :: l) <-> LeLst x l /\ Sorted l. Proof. time (induction l; sauto). Undo. induction l; sintuition. Undo. (* simplification tactics: sintuition, qsimpl, ssimpl *) induction l; qsimpl. Undo. (* From "Sorted (a :: l)" it follows that "LeLst a l" by "H2". *) (* Because "leb x l", "LeLst x (a :: l)" follows from "LeLst a l" by lemma "lem_lelst_trans" *) time (induction l; sauto use: lem_lelst_trans). Undo. (* induction l; sauto use: lem_lelst_trans inv: Sorted, List.Forall. *) (* induction l; sauto use: lem_lelst_trans inv: Sorted. *) (* induction l; sauto use: lem_lelst_trans inv: List.Forall. *) (* induction l; sauto use: lem_lelst_trans inv: Sorted, List.Forall ctrs: -. *) time (induction l; sauto use: lem_lelst_trans inv: Sorted, List.Forall ctrs: Sorted). Undo. time (induction l; sauto lazy: on use: lem_lelst_trans inv: Sorted, List.Forall ctrs: Sorted). (* "lazy: on" turns off all eager heuristics. This may improve performance, but may also make "sauto" fail to solve the goal *) Qed. Lemma lem_lelst_perm_rev {A} {dto : DecTotalOrder A} : forall l1 l2 x, Permutation l1 l2 -> LeLst x l2 -> LeLst x l1. Proof. induction 1; sauto. Qed. Lemma lem_lelst_app {A} {dto : DecTotalOrder A} : forall l1 l2 x, LeLst x l1 -> LeLst x l2 -> LeLst x (l1 ++ l2). Proof. induction 1; sauto. Qed. Hint Resolve lem_lelst_trans lem_lelst_perm_rev lem_lelst_app : lelst. Lemma lem_sorted_concat_1 {A} {dto : DecTotalOrder A} : forall (l l1 l2 : list A) x y, Permutation l (l1 ++ y :: l2) -> Sorted (x :: l1) -> leb x y -> Sorted (y :: l2) -> Sorted l -> Sorted (x :: l). Proof. intros. rewrite lem_lelst_sorted in *. (* sauto db: lelst inv: -. *) split. simp_hyps. eapply lem_lelst_perm_rev; [eassumption|]. apply lem_lelst_app; [assumption|]. constructor; [assumption|]. Check lem_lelst_trans. eauto using lem_lelst_trans. eapply lem_lelst_trans; eassumption. (* Here, "sauto" needs to apply a constructor of "List.Forall", which works on a goal with head "LeLst", but then creates a goal with head "List.Forall" which does not resolve with the "lem_lelst_trans" lemma according to how "eauto" performs resolution *) Restart. intros. rewrite lem_lelst_sorted in *. sauto use: lem_lelst_trans, lem_lelst_perm_rev, lem_lelst_app inv: -. (* "use:" adds the given lemmas to the context, while for lemmas from a hint database only actions associated with the hints are performed in exactly the same way as by "eauto" *) Qed. Lemma lem_lelst_nil {A} {dto : DecTotalOrder A} : forall x, LeLst x []. Proof. sauto. Qed. Lemma lem_lelst_cons {A} {dto : DecTotalOrder A} : forall x y l, LeLst x l -> leb x y -> LeLst x (y :: l). Proof. sauto. Qed. Hint Resolve lem_lelst_nil lem_lelst_cons : lelst. Lemma lem_sorted_concat_2 {A} {dto : DecTotalOrder A} : forall (l l1 l2 : list A) x y, Permutation l (x :: l1 ++ l2) -> Sorted (x :: l1) -> leb y x -> Sorted (y :: l2) -> Sorted l -> Sorted (y :: l). Proof. intros. rewrite lem_lelst_sorted in *. sauto db: lelst inv: -. Qed. Program Fixpoint merge {A} {dto : DecTotalOrder A} (l1 l2 : {l | Sorted l}) {measure (List.length l1 + List.length l2)} : {l | Sorted l /\ Permutation l (l1 ++ l2)} := match l1 with | [] => l2 | h1 :: t1 => match l2 with | [] => l1 | h2 :: t2 => if leb_total_dec h1 h2 then h1 :: merge t1 l2 else h2 :: merge l1 t2 end end. Next Obligation. sauto db: list. Qed. Next Obligation. eauto using lem_sorted_tail. Qed. Next Obligation. sauto use: lem_sorted_concat_1. (* What happened here? *) Undo. simpl_sigma. (* Heuristic simplifications for sigma types are performed by default (controlled by the "sig:" option) *) sauto use: lem_sorted_concat_1. Qed. Next Obligation. eauto using lem_sorted_tail. Qed. Next Obligation. simpl; lia. Qed. Next Obligation. split. - sauto use: lem_sorted_concat_2. - (* sauto use: List.app_comm_cons, Permutation_cons_app. *) simpl_sigma. rewrite List.app_comm_cons. apply Permutation_cons_app. intuition. (* at this point "sauto" would of course also solve the goal *) Qed. Program Fixpoint split {A} (l : list A) {measure (length l)} : { (l1, l2) : list A * list A | length l1 + length l2 = length l /\ length l1 <= length l2 + 1 /\ length l2 <= length l1 + 1 /\ Permutation l (l1 ++ l2) } := match l with | [] => ([], []) | [x] => ([x], []) | x :: y :: t => match split t with | (l1, l2) => (x :: l1, y :: l2) end end. Solve Obligations with sauto use: Permutation_cons_app. Compute ` (split [1; 2; 3; 4; 5; 6; 7; 8; 9]). Lemma lem_split {A} : forall l : list A, 2 <= List.length l -> forall l1 l2, (l1, l2) = ` (split l) -> List.length l1 < List.length l /\ List.length l2 < List.length l. Proof. sauto. Qed. Ltac use_lem_split := match goal with | [ H: (?l1, ?l2) = ` (split ?l) |- _ ] => let Hl := fresh "H" in assert (Hl: 2 <= length l); [ destruct l as [|? [| ? ?]]; simpl | generalize (lem_split l Hl l1 l2) ]; hauto end. Obligation Tactic := idtac. Program Fixpoint mergesort {A} {dto : DecTotalOrder A} (l : list A) {measure (List.length l)} : {l' | Sorted l' /\ Permutation l' l} := match l with | [] => [] | [x] => [x] | _ => match split l with | (l1, l2) => merge (mergesort l1) (mergesort l2) end end. Next Obligation. sauto. Qed. Next Obligation. sauto. Qed. Next Obligation. (* sauto. *) program_simpl. (* sauto use: @lem_split. *) use_lem_split. Qed. Next Obligation. sauto. Qed. Next Obligation. program_simpl; use_lem_split. Qed. Next Obligation. sauto. Qed. Next Obligation. (* simpl. *) split. - sauto. - (* simpl_sigma. *) time hauto use: Permutation_app, Permutation_sym, perm_trans. (* "hauto" is just "sauto inv: - ctrs: -" *) Undo. time qauto use: Permutation_app, Permutation_sym, perm_trans. (* "qauto" is "sauto" with various options which make it much weaker but typically much faster *) Qed. Next Obligation. sauto. Qed. Next Obligation. program_simpl. Defined. Compute ` (mergesort [2; 7; 3; 1; 4; 6; 5; 8; 0; 8]). coqhammer-1.3.3-9.2/examples/tutorial/sauto/order.v000066400000000000000000000046771522316141700221360ustar00rootroot00000000000000From Hammer Require Import Tactics Reflect. From Stdlib Require List. Open Scope list_scope. Import List.ListNotations. Class DecTotalOrder (A : Type) := { leb : A -> A -> bool; leb_total_dec : forall x y, {leb x y}+{leb y x}; leb_antisym : forall x y, leb x y -> leb y x -> x = y; leb_trans : forall x y z, leb x y -> leb y z -> leb x z }. Arguments leb {A _}. Arguments leb_total_dec {A _}. Arguments leb_antisym {A _}. Arguments leb_trans {A _}. Definition eq_dec {A} {dto : DecTotalOrder A} : forall x y : A, {x = y}+{x <> y}. intros x y. sdestruct (leb x y). (* The "sdestruct" tactic from the Tactics module destructs boolean terms in the "right" way *) (* "sauto" tries to invert/destruct only the hypotheses - it will not normally try to eliminate composite terms unless they occur as discriminees in match expressions *) - sdestruct (leb y x). + eauto using leb_antisym. + (* firstorder. *) (* easy. *) (* eauto. *) (* right. intro. subst. contradiction. *) sauto. (* This is a simple proof, but standard Coq automation tactics can't find it because it requires a combination of proof search with equality reasoning. *) - sdestruct (leb y x). + sauto. + destruct (leb_total_dec x y); auto. Defined. (* "sauto" searches for proofs in intuitionistic logic, which can equivalently be seen as program synthesis. "eq_dec" is a certified computable function which decides whether the equality holds or not. *) From Stdlib Require Import Recdef. (* for Function *) Function lexb {A} {dto : DecTotalOrder A} (l1 l2 : list A) : bool := match l1 with | [] => true | x :: l1' => match l2 with | [] => false | y :: l2' => if eq_dec x y then lexb l1' l2' else leb x y end end. Instance dto_list {A} {dto_a : DecTotalOrder A} : DecTotalOrder (list A). Proof. apply Build_DecTotalOrder with (leb := lexb). - induction x; sauto. - intros x y. functional induction (lexb x y). + (* sauto. *) sauto inv: list. + sauto. + sauto. + (* sauto. *) (* ssimpl inv: -. *) sauto inv: - use: leb_antisym. - intros x y. functional induction (lexb x y); sauto. Defined. Instance dto_nat : DecTotalOrder nat. Proof. apply Build_DecTotalOrder with (leb := Nat.leb); induction x; sauto. Defined. Compute leb [1; 2; 3] [1; 4; 5; 6]. Compute leb [1; 2; 3] [1]. Compute leb 2 3. Compute leb 3 2. coqhammer-1.3.3-9.2/justfile000066400000000000000000000055421522316141700155600ustar00rootroot00000000000000# CoqHammer release automation. # # See scripts/release-lib.sh for the naming conventions and # scripts/make-release.sh / scripts/publish-opam.sh for details. # # just release minor # bump 1.3.2 -> 1.4.0 and release for the # # current branch's Rocq version # just release patch # 1.3.2 -> 1.3.3 # just release major # 1.3.2 -> 2.0.0 # just release none # release the current version unchanged for a # # new Rocq (check out the rocq- branch first) # just release none --trivial # ... skipping the triviality confirmation # just publish-opam 1.3.2+9.1 # publish a released version on opam (fork) # just migrate 9.2 # branch rocq-9.2 off the current branch and # # retarget its version strings / opam files _default: @just --list # Install both packages, then run the quick smoke-test suite. check: make install make quicktest # Bump the CoqHammer version (patch|minor|major, or none to keep it) and publish # a GitHub release for this branch's Rocq. `none` ports the current release to a # newly checked-out rocq- dev branch: it scans the new commits and asks you # to confirm they are trivial before proceeding. Extra args after are # forwarded to make-release.sh (e.g. --trivial to skip that confirmation). [doc('Cut a GitHub release (level: patch|minor|major|none); extra args forwarded')] release level *args: ./scripts/make-release.sh {{level}} {{args}} # Add opam packages for a published release to the opam-coq-archive fork. publish-opam version: ./scripts/publish-opam.sh {{version}} # Merge into the CURRENT branch, automatically absorbing the trivial # per-branch version-token differences in the *.opam / dune / META.* files. # Run it on the branch you are merging INTO; any real conflicts are left in # progress on it for you to resolve and commit. E.g. on `master` (which tracks # unstable Rocq): `just sync rocq-9.1`. [doc('Merge into the current branch, absorbing per-branch version tokens')] sync source: ./scripts/sync-branch.sh {{source}} # Migrate the project to a new Rocq version. Run it on the branch to migrate # FROM (typically `master`): creates a new local branch `rocq-`, # rewrites the per-Rocq version strings and *.opam files on it, and -- when the # AGM project config tree is present -- adds a `config/rocq-` workspace # config selecting the toolchain (opam package, or a from-source build when the # version is not yet on opam). Branch only: no worktree is created and no build # is run; review and push the branch (and the config commit) yourself. # E.g. on master: just migrate 9.2 [doc('Branch rocq- off the current branch and retarget its version tokens')] migrate version: ./scripts/migrate.sh {{version}} coqhammer-1.3.3-9.2/scripts/000077500000000000000000000000001522316141700154715ustar00rootroot00000000000000coqhammer-1.3.3-9.2/scripts/make-release.sh000077500000000000000000000111571522316141700203700ustar00rootroot00000000000000#!/usr/bin/env bash # # make-release.sh [--trivial] # # Cuts a CoqHammer release from the current development branch and # publishes it on GitHub (branch + tag + GitHub release). # # patch|minor|major bump the CoqHammer version and release it for the # current branch's Rocq version. # none release the *current* CoqHammer version for the # current branch's Rocq version (used when porting an # existing release to a new Rocq: check out the new # rocq- dev branch first, then run this). # # With `none` the script scans the commits that are new relative to the # previous release of the same CoqHammer version and asks you to confirm # they are trivial (Rocq-API / doc / style only). If they are not, a version # bump is required -- re-run with patch|minor|major. # # --trivial skip the interactive triviality confirmation (assume yes). # # Everything after the (optional) confirmation runs unattended, including # pushing the branch and tag and creating the GitHub release. set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/release-lib.sh" LEVEL="${1:-}" [ -n "$LEVEL" ] || die "usage: make-release.sh [--trivial]" ASSUME_TRIVIAL=0 [ "${2:-}" = "--trivial" ] && ASSUME_TRIVIAL=1 cd "$REPO_ROOT" command -v gh >/dev/null || die "the GitHub CLI 'gh' is required" require_clean_worktree DEV_BRANCH="$(current_branch)" case "$DEV_BRANCH" in rocq-*) ;; *) die "not on a rocq- development branch (on '$DEV_BRANCH')" ;; esac ROCQ="$(rocq_version_from_opam)" OLD_CVER="$(current_cver)" CVER="$(bump_cver "$OLD_CVER" "$LEVEL")" REL_BRANCH="v${CVER}-rocq${ROCQ}" TAG="v${CVER}+${ROCQ}" info "dev branch: $DEV_BRANCH" info "Rocq version: $ROCQ" info "CoqHammer: $OLD_CVER -> $CVER (bump: $LEVEL)" info "release branch: $REL_BRANCH" info "release tag: $TAG" git show-ref --verify --quiet "refs/heads/$REL_BRANCH" \ && die "branch $REL_BRANCH already exists" git rev-parse -q --verify "refs/tags/$TAG" >/dev/null \ && die "tag $TAG already exists" # --- Triviality gate for the "port existing release to new Rocq" case ------ if [ "$LEVEL" = none ]; then # Previous release of the same CoqHammer version, on any other Rocq line. PREV_TAG="$(git tag --list "v${CVER}+*" | grep -vx "$TAG" | sort -V | tail -1 || true)" if [ -z "$PREV_TAG" ]; then info "no previous v${CVER}+* release found to compare against" else echo >&2 info "changes on $DEV_BRANCH not in $PREV_TAG:" git --no-pager log --oneline "$PREV_TAG..HEAD" >&2 || true echo >&2 git --no-pager diff --stat "$PREV_TAG..HEAD" >&2 || true echo >&2 fi if [ "$ASSUME_TRIVIAL" -ne 1 ]; then read -r -p "Are these changes trivial (Rocq-port / doc / style only)? [y/N] " ans case "$ans" in y | Y | yes | YES) ;; *) die "non-trivial changes: a version bump is required (run: just release patch|minor|major)" ;; esac fi fi # --- Create the release branch and apply the deterministic edits ----------- info "creating release branch $REL_BRANCH" git checkout -q -b "$REL_BRANCH" for f in coq-hammer.opam coq-hammer-tactics.opam; do sed -i \ -e "s|^version: \"${ROCQ}\.dev\"|version: \"${CVER}+${ROCQ}\"|" \ -e "s|^maintainer: \".*\"|maintainer: \"${RELEASE_MAINTAINER}\"|" \ "$f" done # README.md: title line + Docker CI badge branch reference. sed -i \ -e "1s|.*|CoqHammer ${CVER} for Rocq ${ROCQ}|" \ -e "s|branch=rocq-${ROCQ}|branch=${REL_BRANCH}|g" \ README.md # Version string displayed by the `Hammer_version` command: drop the # "(dev)" placeholder for the released version (mirrors migrate.sh). sed -i \ -e "s|^let hammer_version_string = \".*\"|let hammer_version_string = \"CoqHammer ${CVER} for Rocq ${ROCQ}\"|" \ src/plugin/g_hammer.mlg git add coq-hammer.opam coq-hammer-tactics.opam README.md src/plugin/g_hammer.mlg git commit -q -m "Release CoqHammer ${CVER} for Rocq ${ROCQ}" git tag -a "$TAG" -m "CoqHammer ${CVER} for Rocq ${ROCQ}" # --- Publish on GitHub ----------------------------------------------------- info "pushing $REL_BRANCH and $TAG to origin" git push -q origin "$REL_BRANCH" git push -q origin "refs/tags/$TAG" notes="$(changes_section "$CVER" "$ROCQ")" info "creating GitHub release $TAG" if [ -n "$notes" ]; then gh release create "$TAG" --repo "$GH_REPO" \ --title "$REL_BRANCH" --notes "$notes" else gh release create "$TAG" --repo "$GH_REPO" \ --title "$REL_BRANCH" --generate-notes fi git checkout -q "$DEV_BRANCH" info "done. Published tag $TAG." info "Next: publish on opam with just publish-opam ${CVER}+${ROCQ}" coqhammer-1.3.3-9.2/scripts/migrate.sh000077500000000000000000000372711522316141700174720ustar00rootroot00000000000000#!/usr/bin/env bash # # migrate.sh # # Migrate CoqHammer to a new Rocq version. Run it on the branch you want to # branch FROM -- typically `master` (which tracks unstable Rocq): # # git checkout master # just migrate 9.2 # # It performs three local, side-effect-contained steps and NOTHING ELSE (no # network writes, no worktrees, no `agm` invocations, no builds): # # 1. Creates a new local branch `rocq-` off the current branch, WITHOUT # touching the working tree (the commit is built with git plumbing, so the # branch you are on is left exactly as it was and no worktree is created). # # 2. On that branch, rewrites the per-Rocq-version tokens that distinguish a # `rocq-` development branch from `master` -- the same tokens the # sync merge driver (scripts/sync-merge-driver.sh) normalizes, so a later # `just sync` is a no-op on them: # * the two *.opam files: version ".dev" and the Rocq dependency # lines `"rocq-core" {>= "" & < "~"}` and the matching # `"rocq-stdlib"` (the deprecated `coq` package is no longer used); # * the README title line, the CI-badge branch, and the requirement # label + homepage URL; # * the docker image tag in the Docker CI workflow, plus the `rocq-*` / # `coq*` push triggers so the new branch is actually built by CI; # * the `hammer_version_string` banner in src/plugin/g_hammer.mlg. # # 3. If the AGM project config tree exists ($PROJ_DIR/config), adds a # workspace config `$PROJ_DIR/config/rocq-/env.sh` selecting the Rocq # toolchain for the new branch. When rocq-core is on opam it uses the # opam packages: if rocq-stdlib is also published the opam-file # constraints solve on their own, otherwise the newest available rocq-core # and rocq-stdlib are pinned via COQHAMMER_ROCQ_PACKAGES (an older stdlib # builds against the newer core). When Rocq is not on opam at all it # falls back to a from-source build pinned to a git ref resolved from the # official Rocq repository (latest release tag, else the version branch). # The config change is committed in the config repo. # # Everything is local: review the new branch (and the config commit) and push # when satisfied. To build it, open a workspace for `rocq-` yourself. set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/release-lib.sh" ROCQ_SOURCE_REPO="https://github.com/rocq-prover/rocq.git" STDLIB_SOURCE_REPO="https://github.com/rocq-prover/stdlib.git" V="${1:-}" [ -n "$V" ] || die "usage: migrate.sh (e.g. migrate.sh 9.2)" case "$V" in [0-9]*.[0-9]*) ;; *) die "version must be of the form , e.g. 9.2 (got '$V')" ;; esac [[ "$V" =~ ^[0-9]+\.[0-9]+$ ]] || die "version must be exactly major.minor, e.g. 9.2 (got '$V')" NEXT="$(next_rocq "$V")" # 9.2 -> 9.3, 8.20 -> 8.21 NEW_BRANCH="rocq-${V}" # For matching version numbers inside regexes. VRE="${V//./\\.}" cd "$REPO_ROOT" command -v git >/dev/null || die "git is required" require_clean_worktree SOURCE="$(current_branch)" [ "$SOURCE" != "HEAD" ] || die "detached HEAD; check out the branch to migrate FROM first" [ "$SOURCE" != "$NEW_BRANCH" ] || die "already on '$NEW_BRANCH'" git show-ref --verify --quiet "refs/heads/$NEW_BRANCH" \ && die "branch '$NEW_BRANCH' already exists" info "migrating to Rocq $V" info "source branch: $SOURCE" info "new branch: $NEW_BRANCH" # --------------------------------------------------------------------------- # 1. Resolve the toolchain for the new branch: opam package vs from-source. # --------------------------------------------------------------------------- # rocq_core_pkg: the opam core/meta package name for Rocq . Since the Rocq # rename (Rocq >= 9.0) it is rocq-core; the deprecated `coq` meta-package is used # only for the older Coq (< 9.0) branches. rocq_core_pkg() { [ "${V%%.*}" -ge 9 ] 2>/dev/null && echo rocq-core || echo coq } # opam_pkg_versions : all released opam versions of , one per line. opam_pkg_versions() { command -v opam >/dev/null || return 1 opam show "$1" -f all-versions 2>/dev/null | tr ' ,' '\n\n' | grep -E '^[0-9]' } # opam_newest_matching : newest opam version of in the # line (e.g. 9.2.1 for rocq-core 9.2); empty if none. opam_newest_matching() { opam_pkg_versions "$1" | grep -E "^${2//./\\.}(\.|$)" | sort -V | tail -1 } # opam_newest : newest opam version of overall; empty if none. opam_newest() { opam_pkg_versions "$1" | sort -V | tail -1 } # resolve_source_ref : print a git ref for a source build of , # preferring the latest stable release tag V., then the version branch # v, then the latest pre-release tag V+<...>. Non-zero if none found. resolve_source_ref() { local url="$1" refs t refs="$(git ls-remote --heads --tags "$url" 2>/dev/null)" || return 1 [ -n "$refs" ] || return 1 t="$(printf '%s\n' "$refs" \ | sed -nE "s#.*refs/tags/(V${VRE}\.[0-9]+)\$#\1#p" | sort -V | tail -1)" if [ -n "$t" ]; then printf '%s\n' "$t"; return 0; fi if printf '%s\n' "$refs" | grep -qE "refs/heads/v${VRE}\$"; then printf 'v%s\n' "$V"; return 0 fi t="$(printf '%s\n' "$refs" \ | sed -nE "s#.*refs/tags/(V${VRE}\+[A-Za-z0-9.]+)\$#\1#p" | sort -V | tail -1)" if [ -n "$t" ]; then printf '%s\n' "$t"; return 0; fi return 1 } TOOLCHAIN="" # "opam" or "source" CORE_PKG="$(rocq_core_pkg)" CORE_OPAM_VER="" # newest opam version of the core package ROCQ_OPAM_PACKAGES="" # explicit pin list for env.sh; empty => rely on constraints STDLIB_OPAM_GUESSED=0 # 1 if an older-than- stdlib had to be pinned ROCQ_REF="" STDLIB_REF="" STDLIB_GUESSED=0 CORE_OPAM_VER="$(opam_newest_matching "$CORE_PKG" "$V" || true)" if [ -n "$CORE_OPAM_VER" ]; then TOOLCHAIN="opam" if [ "$CORE_PKG" = "rocq-core" ]; then # Rocq >= 9.0: the standard library is a separate opam package. If rocq-stdlib # is not yet published for this , pin the newest available stdlib -- an # older stdlib builds and loads against the newer core -- and let setup.sh # install it while ignoring the opam-file constraints. When rocq-stdlib # IS available, no pin is needed and the constraints solve on their own. if [ -z "$(opam_newest_matching rocq-stdlib "$V" || true)" ]; then stdlib_ver="$(opam_newest rocq-stdlib || true)" if [ -n "$stdlib_ver" ]; then ROCQ_OPAM_PACKAGES="rocq-core.${CORE_OPAM_VER} rocq-stdlib.${stdlib_ver}" STDLIB_OPAM_GUESSED=1 fi fi fi if [ -n "$ROCQ_OPAM_PACKAGES" ]; then info "toolchain: opam, pinned packages: $ROCQ_OPAM_PACKAGES" else info "toolchain: opam package ${CORE_PKG}.${CORE_OPAM_VER} (via opam-file constraints)" fi else TOOLCHAIN="source" ROCQ_REF="$(resolve_source_ref "$ROCQ_SOURCE_REPO")" \ || die "Rocq $V is not on opam and no matching branch/tag found on $ROCQ_SOURCE_REPO" # Match the stdlib ref independently (since Rocq 9.0 it is a separate repo and # its refs do not always mirror Rocq's, e.g. patch tags may be missing). if STDLIB_REF="$(resolve_source_ref "$STDLIB_SOURCE_REPO")"; then :; else STDLIB_REF="$ROCQ_REF" STDLIB_GUESSED=1 fi info "toolchain: source build, Rocq ref '$ROCQ_REF', stdlib ref '$STDLIB_REF'" fi # Lower bound for the rocq-stdlib opam-file dependency. Normally the target Rocq # , but rocq-stdlib usually lags rocq-core on opam; when is not yet # published, fall back to the newest available stdlib line so the constraint # stays satisfiable -- an older stdlib builds and loads against the newer core, # and opam CI resolves rocq-hammer's deps against the published packages. rocq-core # keeps the exact lower bound (that package is published). Defaults to # when opam is unavailable (a source build ignores the opam constraints anyway). STDLIB_LB="$V" if [ -z "$(opam_newest_matching rocq-stdlib "$V" || true)" ]; then _stdlib_newest="$(opam_newest rocq-stdlib || true)" if [ -n "$_stdlib_newest" ]; then STDLIB_LB="$(printf '%s\n' "$_stdlib_newest" | grep -oE '^[0-9]+\.[0-9]+')" fi fi info "opam constraints: rocq-core >= $V, rocq-stdlib >= $STDLIB_LB (both < ${NEXT}~)" # --------------------------------------------------------------------------- # 2. Build the new branch by rewriting the version tokens, without a worktree. # Each file is read from $SOURCE, transformed in a temp file, hashed into a # blob, and staged in a scratch index; the resulting tree becomes one commit # whose parent is $SOURCE. The current working tree is never touched. # --------------------------------------------------------------------------- TMPDIR_MIG="$(mktemp -d)" trap 'rm -rf "$TMPDIR_MIG"' EXIT IDX="$TMPDIR_MIG/index" export GIT_INDEX_FILE="$IDX" git read-tree "$SOURCE" # The transforms are flavor-agnostic: they map either the `master` flavor # ("dev" / "Coq master" / rocq-stdlib) or an existing `rocq-` flavor to the # target Rocq , so migrate works from any branch. transform_opam() { sed -i -E "s#^version: \".*\"#version: \"${V}.dev\"#" "$1" # Replace whatever Rocq/Coq dependency line(s) the source flavor carries -- # master's single "rocq-stdlib" line, an older branch's single "coq" line, or # the current two-line "rocq-core"/"rocq-stdlib" form -- with the canonical # two-line dependency for the target Rocq . rocq-stdlib takes the lower # bound $STDLIB_LB (, or an older published line when lags on opam). # (`nxt`, not `next`, since `next` is an awk statement.) awk -v v="$V" -v nxt="$NEXT" -v slb="$STDLIB_LB" ' /^[[:space:]]*"(rocq-core|rocq-stdlib|coq)"[[:space:]]*[{]/ { if (!done) { print " \"rocq-core\" {>= \"" v "\" & < \"" nxt "~\"}" print " \"rocq-stdlib\" {>= \"" slb "\" & < \"" nxt "~\"}" done = 1 } next } { print } ' "$1" > "$1.mig" && mv "$1.mig" "$1" } transform_readme() { sed -i -E \ -e "1s#.*#CoqHammer (dev) for Rocq ${V} (use other branches for other versions of Rocq)#" \ -e "s#([?&]branch=)[A-Za-z0-9._-]+#\1${NEW_BRANCH}#g" \ -e "s#\[(Rocq|Coq) [^]]*\]\([^)]*\)#[Rocq ${V}](https://rocq-prover.org/)#g" \ "$1" } transform_docker() { sed -i -E "s#(rocq/rocq-prover:)[A-Za-z0-9._-]+#\1${V}#g" "$1" # Ensure the new branch (and any rocq-*/coq* branch) triggers Docker CI. if ! grep -qE "rocq-\*" "$1"; then awk ' /^ - master$/ && !done { print print " - " "\047rocq-*\047" print " - " "\047coq*\047" done = 1 next } { print } ' "$1" > "$1.mig" && mv "$1.mig" "$1" fi } transform_mlg() { sed -i -E \ "s#^let hammer_version_string = \".*\"#let hammer_version_string = \"CoqHammer (dev) for Rocq ${V}\"#" \ "$1" } # stage : transform $SOURCE: and stage the result. stage() { local path="$1" fn="$2" mode blob work git cat-file -e "$SOURCE:$path" 2>/dev/null \ || { info " skip (absent on $SOURCE): $path"; return 0; } work="$TMPDIR_MIG/work" git show "$SOURCE:$path" > "$work" "$fn" "$work" mode="$(git ls-tree "$SOURCE" -- "$path" | awk '{print $1}')" blob="$(git hash-object -w "$work")" git update-index --cacheinfo "${mode},${blob},${path}" } info "rewriting version tokens on $NEW_BRANCH:" stage coq-hammer.opam transform_opam stage coq-hammer-tactics.opam transform_opam stage README.md transform_readme stage .github/workflows/docker-action.yml transform_docker stage src/plugin/g_hammer.mlg transform_mlg NEW_TREE="$(git write-tree)" SOURCE_TREE="$(git rev-parse "${SOURCE}^{tree}")" if [ "$NEW_TREE" = "$SOURCE_TREE" ]; then die "no version tokens changed -- is '$SOURCE' already flavored for Rocq $V?" fi NEW_COMMIT="$(git commit-tree "$NEW_TREE" -p "$SOURCE" \ -m "Set up ${NEW_BRANCH} development branch (Rocq ${V})")" git branch "$NEW_BRANCH" "$NEW_COMMIT" unset GIT_INDEX_FILE info "created branch $NEW_BRANCH at $(git rev-parse --short "$NEW_COMMIT")" # --------------------------------------------------------------------------- # 3. Add the AGM workspace config for the new branch, if the config tree is # present. This lives in a separate git repo ($PROJ_DIR/config); commit the # new branch config there. # --------------------------------------------------------------------------- proj="${PROJ_DIR:-}" if [ -z "$proj" ]; then # Fall back to the AGM split layout: repo/ (or worktrees//) under $proj. case "$REPO_ROOT" in */worktrees/*) proj="${REPO_ROOT%/worktrees/*}" ;; */repo) proj="${REPO_ROOT%/repo}" ;; esac fi CFG_DIR="${proj:+$proj/config}" if [ -n "$CFG_DIR" ] && [ -d "$CFG_DIR" ]; then branch_cfg="$CFG_DIR/$NEW_BRANCH" env_file="$branch_cfg/env.sh" mkdir -p "$branch_cfg" if [ "$TOOLCHAIN" = "opam" ]; then if [ -n "$ROCQ_OPAM_PACKAGES" ]; then cat > "$env_file" < "$env_file" < "$env_file" fi if git -C "$CFG_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then git -C "$CFG_DIR" add -- "$NEW_BRANCH" if git -C "$CFG_DIR" diff --cached --quiet -- "$NEW_BRANCH"; then info "config for $NEW_BRANCH already up to date in $CFG_DIR" else git -C "$CFG_DIR" commit -q -m "Add config for $NEW_BRANCH (Rocq $V)" \ -- "$NEW_BRANCH" info "committed workspace config: $env_file" fi else info "wrote workspace config (not a git repo, left uncommitted): $env_file" fi if [ "$STDLIB_GUESSED" -eq 1 ]; then info "WARNING: stdlib source ref guessed as '$STDLIB_REF'; verify $env_file" fi if [ "$STDLIB_OPAM_GUESSED" -eq 1 ]; then info "WARNING: rocq-stdlib $V is not on opam; pinned an older stdlib in" info " $ROCQ_OPAM_PACKAGES -- verify $env_file and drop the pin" info " once rocq-stdlib $V is published." fi else info "no AGM config tree at \${PROJ_DIR}/config -- skipping workspace config" fi # --------------------------------------------------------------------------- echo >&2 info "done. Review and push when satisfied:" info " git log -p $NEW_BRANCH -1" info " git push origin $NEW_BRANCH" if [ -n "$CFG_DIR" ] && [ -d "$CFG_DIR" ]; then info " git -C $CFG_DIR push" fi coqhammer-1.3.3-9.2/scripts/publish-opam.sh000077500000000000000000000140261522316141700204330ustar00rootroot00000000000000#!/usr/bin/env bash # # publish-opam.sh # # a published CoqHammer opam version, i.e. + # e.g. 1.3.2+9.1 (must match a pushed GitHub tag v<...>). # # Adds coq-hammer and coq-hammer-tactics opam packages for the given release # to a local checkout of the fork # # git@github.com:lukaszcz/opam-coq-archive.git # # on a fresh branch. The fork's master is first synced with upstream # (coq/opam-coq-archive). Each new opam file is derived from the most recent # existing entry of the same package by updating exactly four things: # # * the Rocq dependency (rocq-core >= & rocq-stdlib # >= , both # < ~; a legacy "coq" line in an # older template is replaced) # * the "date:" tag (today) # * the release tarball URL (.../tags/v+.tar.gz) # * the sha512 checksum (computed from that tarball) # # The branch is pushed to the fork. No pull request is opened. # # Env: # OPAM_ARCHIVE_DIR where to keep the fork checkout # (default: $HOME/.cache/coqhammer/opam-coq-archive) # OPAM_PUSH=0 do everything locally, do not push the branch set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/release-lib.sh" VERSTR="${1:-}" [ -n "$VERSTR" ] || die "usage: publish-opam.sh + (e.g. 1.3.2+9.1)" case "$VERSTR" in *+*) ;; *) die "version must be of the form +, e.g. 1.3.2+9.1" ;; esac CVER="${VERSTR%%+*}" ROCQ="${VERSTR##*+}" ROCQ_NEXT="$(next_rocq "$ROCQ")" TAG="v${VERSTR}" TODAY="$(date +%F)" # Lower bound for the rocq-stdlib dependency: normally , but rocq-stdlib # usually lags rocq-core on opam. If is not yet published, fall back to # the newest available stdlib line so the published constraint is satisfiable # (an older stdlib builds and loads against the newer core). rocq-core keeps the # exact lower bound. Defaults to when opam or the rocq packages # are unavailable. STDLIB_LB="$ROCQ" if command -v opam >/dev/null 2>&1; then _stdlib_all="$(opam show rocq-stdlib -f all-versions 2>/dev/null | tr ' ,' '\n\n' | grep -E '^[0-9]')" if ! printf '%s\n' "$_stdlib_all" | grep -qE "^${ROCQ//./\\.}(\.|$)"; then _stdlib_newest="$(printf '%s\n' "$_stdlib_all" | sort -V | tail -1)" [ -n "$_stdlib_newest" ] && STDLIB_LB="$(printf '%s\n' "$_stdlib_newest" | grep -oE '^[0-9]+\.[0-9]+')" fi fi ARCHIVE_DIR="${OPAM_ARCHIVE_DIR:-$HOME/.cache/coqhammer/opam-coq-archive}" BRANCH="release-coq-hammer-${VERSTR}" command -v git >/dev/null || die "git is required" command -v curl >/dev/null || die "curl is required" command -v sha512sum >/dev/null || die "sha512sum is required" # --- Obtain / sync the fork ------------------------------------------------ if [ ! -d "$ARCHIVE_DIR/.git" ]; then info "cloning fork into $ARCHIVE_DIR" mkdir -p "$(dirname "$ARCHIVE_DIR")" git clone "$OPAM_FORK_URL" "$ARCHIVE_DIR" fi cd "$ARCHIVE_DIR" git remote get-url upstream >/dev/null 2>&1 || git remote add upstream "$OPAM_UPSTREAM_URL" info "syncing fork master with upstream" git fetch -q upstream git fetch -q origin git checkout -q master 2>/dev/null || git checkout -q -b master origin/master git reset --hard upstream/master if [ "${OPAM_PUSH:-1}" = 1 ]; then git push -q --force-with-lease origin master fi git show-ref --verify --quiet "refs/heads/$BRANCH" && git branch -q -D "$BRANCH" info "creating branch $BRANCH" git checkout -q -b "$BRANCH" # --- Compute the tarball checksum ------------------------------------------ TARBALL_URL="https://github.com/${GH_REPO}/archive/refs/tags/${TAG}.tar.gz" info "downloading $TARBALL_URL" tmp="$(mktemp)" trap 'rm -f "$tmp"' EXIT curl -fsSL "$TARBALL_URL" -o "$tmp" || die "could not download $TARBALL_URL (is the tag pushed?)" SHA512="$(sha512sum "$tmp" | cut -d' ' -f1)" info "sha512 = $SHA512" # --- Create the two package entries ---------------------------------------- add_package() { local pkg="$1" local dir="released/packages/$pkg" local newdir="$dir/$pkg.$VERSTR" local template # Prefer the most recent entry of the same CoqHammer version; otherwise the # most recent entry of the package overall. template="$(ls -d "$dir/$pkg.$CVER+"* 2>/dev/null | sort -V | tail -1 || true)" [ -n "$template" ] || template="$(ls -d "$dir/$pkg."* 2>/dev/null | sort -V | tail -1 || true)" [ -n "$template" ] || die "no existing $pkg entry to use as a template" [ -e "$newdir" ] && die "$newdir already exists" info "$pkg: templating from $(basename "$template")" mkdir -p "$newdir" cp "$template/opam" "$newdir/opam" # Replace whatever Rocq/Coq dependency line(s) the template carries -- an old # entry's single "coq" line or a newer entry's two-line "rocq-core"/"rocq-stdlib" # form -- with the canonical two-line dependency for this release's Rocq . # (`nxt`, not `next`, since `next` is an awk statement.) awk -v v="$ROCQ" -v nxt="$ROCQ_NEXT" -v slb="$STDLIB_LB" ' /^[[:space:]]*"(rocq-core|rocq-stdlib|coq)"[[:space:]]*[{]/ { if (!done) { print " \"rocq-core\" {>= \"" v "\" & < \"" nxt "~\"}" print " \"rocq-stdlib\" {>= \"" slb "\" & < \"" nxt "~\"}" done = 1 } next } { print } ' "$newdir/opam" > "$newdir/opam.pub" && mv "$newdir/opam.pub" "$newdir/opam" sed -i \ -e "s|\"date:[0-9-]*\"|\"date:${TODAY}\"|" \ -e "s|archive/refs/tags/v[^\"]*|archive/refs/tags/${TAG}.tar.gz|" \ -e "s|checksum: \"sha512=[0-9a-fA-F]*\"|checksum: \"sha512=${SHA512}\"|" \ "$newdir/opam" git add "$newdir/opam" } add_package coq-hammer add_package coq-hammer-tactics git commit -q -m "Add coq-hammer(-tactics) ${VERSTR}" if [ "${OPAM_PUSH:-1}" = 1 ]; then info "pushing branch $BRANCH to fork" git push -q -u origin "$BRANCH" info "done. Branch pushed to the fork; open a PR to coq/opam-coq-archive manually." else info "done. Branch $BRANCH created locally in $ARCHIVE_DIR (not pushed)." fi coqhammer-1.3.3-9.2/scripts/release-lib.sh000077500000000000000000000103431522316141700202150ustar00rootroot00000000000000# shellcheck shell=bash # # Shared helpers for the CoqHammer release scripts. # # Conventions used throughout (derived from the existing tags/branches): # # dev branch rocq- e.g. rocq-9.1 # release branch v-rocq e.g. v1.3.2-rocq9.1 # release tag v+ e.g. v1.3.2+9.1 # opam version + e.g. 1.3.2+9.1 # # where CVER is the CoqHammer version (X.Y.Z) and ROCQ is the Rocq # major.minor version (e.g. 9.1, 8.20). On a development branch the two # .opam files carry the placeholder version ".dev". set -euo pipefail _release_lib_src="${BASH_SOURCE[0]:-}" if [ -n "$_release_lib_src" ]; then REPO_ROOT="$(cd "$(dirname "$_release_lib_src")/.." && pwd)" else REPO_ROOT="$(git rev-parse --show-toplevel)" fi # Maintainer used in the released .opam files (see the published # opam-coq-archive entries and the historic release branches). RELEASE_MAINTAINER="lukaszcz@mimuw.edu.pl" # Upstream opam repository and the fork we publish through. OPAM_UPSTREAM_URL="https://github.com/coq/opam-coq-archive.git" OPAM_FORK_URL="git@github.com:lukaszcz/opam-coq-archive.git" # GitHub repo (owner/name) that hosts the release tarballs. GH_REPO="lukaszcz/coqhammer" die() { echo "error: $*" >&2; exit 1; } info() { echo ">> $*" >&2; } # require_clean_worktree require_clean_worktree() { git -C "$REPO_ROOT" diff --quiet && git -C "$REPO_ROOT" diff --cached --quiet \ || die "working tree is dirty; commit or stash first" } # current_branch current_branch() { git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD; } # rocq_version_from_opam # Reads the Rocq version from the dev-branch placeholder ".dev". rocq_version_from_opam() { local v v="$(sed -n 's/^version: "\(.*\)\.dev"/\1/p' "$REPO_ROOT/coq-hammer.opam")" [ -n "$v" ] || die "coq-hammer.opam version is not '.dev' -- not on a dev branch?" echo "$v" } # current_cver # The most recent released CoqHammer version, read from the GitHub # releases -- the authoritative record of what has actually shipped. # Release tags are v+ (older ones v+coq); the # CoqHammer version is the leading v component, maximised over # all Rocq lines. current_cver() { local v v="$(gh api "repos/${GH_REPO}/releases" --paginate --jq '.[].tag_name' 2>/dev/null \ | grep -oE '^v[0-9]+(\.[0-9]+){1,2}' | sed 's/^v//' | sort -V | tail -1)" [ -n "$v" ] || die "could not read the latest release version from GitHub (${GH_REPO})" normalize_cver "$v" } # normalize_cver X.Y -> X.Y.0 (leaves X.Y.Z untouched) normalize_cver() { case "$(grep -o '\.' <<<"$1" | wc -l)" in 1) echo "$1.0" ;; *) echo "$1" ;; esac } # bump_cver bump_cver() { local ver="$1" level="$2" maj min pat IFS=. read -r maj min pat <<<"$ver" case "$level" in major) echo "$((maj + 1)).0.0" ;; minor) echo "${maj}.$((min + 1)).0" ;; patch) echo "${maj}.${min}.$((pat + 1))" ;; none) echo "$ver" ;; *) die "unknown bump level: $level (expected patch|minor|major|none)" ;; esac } # next_rocq -> next minor (9.1 -> 9.2, 8.20 -> 8.21) next_rocq() { local maj min IFS=. read -r maj min <<<"$1" echo "${maj}.$((min + 1))" } # changes_section # Prints the GitHub release notes for the given version: a plain # "CoqHammer v. for Rocq " line followed by the bullet list # from the "Overview of changes" subsection of that version's CHANGES.md # entry (empty if the entry is absent). CHANGES.md is read only to source # the release notes -- never to determine the version (that comes from the # GitHub releases; see current_cver). changes_section() { local cver="$1" rocq="$2" body body="$(awk -v v="$cver" ' $0 ~ "^CoqHammer v\\. " v "([^0-9]|$)" { insec = 1; next } insec && /^CoqHammer v\. / { exit } insec && !grab && /^Overview of changes/ { getline; grab = 1; next } grab { if ($0 ~ /^(-{3,}|={3,})$/) { have = 0; exit } # underline of the next heading if (have) print buf buf = $0; have = 1 } END { if (have && buf !~ /^[[:space:]]*$/) print buf } ' "$REPO_ROOT/CHANGES.md")" [ -n "$body" ] || return 0 printf 'CoqHammer v. %s for Rocq %s\n\n%s\n' "$cver" "$rocq" "$body" } coqhammer-1.3.3-9.2/scripts/sync-branch.sh000077500000000000000000000150121522316141700202360ustar00rootroot00000000000000#!/usr/bin/env bash # # sync-branch.sh # # Merge into the CURRENT branch, automatically absorbing the # recurring, mechanical per-branch differences between CoqHammer branches -- # the Rocq-version-flavored tokens in the *.opam / dune / META.* build-metadata # files. Run it while checked out on the branch you are merging INTO. # # git checkout master # the branch that tracks unstable Rocq # just sync rocq-9.1 # pull the rocq-9.1 development into it # # (Any two branches work, in either direction: on rocq-9.1 you can likewise # `just sync master` to pull Rocq-API updates back, keeping rocq-9.1's flavor.) # # It uses a direction-agnostic, token-normalizing merge driver # (scripts/sync-merge-driver.sh) that is registered *locally* only for the # duration of the merge: nothing is committed to any branch's tracked state, # and unrelated merges are unaffected. `git rerere` is also enabled so that any # genuine conflict you resolve once is reapplied automatically on the next sync. # # On success the merge is left committed on the current branch for you to # review and push. If real (non-trivial) conflicts remain, the merge is left in # progress on the current branch for you to resolve and commit by hand -- # exactly as a plain `git merge` would. set -euo pipefail # Self-contained on purpose: this script runs on the branch being merged INTO, # which may not (yet) carry the rest of the scripts/ tooling, so it does not # source release-lib.sh. die() { echo "error: $*" >&2; exit 1; } info() { echo ">> $*" >&2; } current_branch() { git rev-parse --abbrev-ref HEAD; } require_clean_worktree() { git diff --quiet && git diff --cached --quiet \ || die "working tree is dirty; commit or stash first" } # Locate the companion merge driver next to THIS script, not via the repo root: # `just sync` may run on a branch that has not yet received the scripts/ tooling # (e.g. the first sync onto master), where $REPO_ROOT/scripts/ would be empty. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" SOURCE="${1:-}" [ -n "$SOURCE" ] || die "usage: sync-branch.sh " git rev-parse --verify --quiet "${SOURCE}^{commit}" >/dev/null \ || die "branch '$SOURCE' does not exist" TARGET="$(current_branch)" [ "$TARGET" != "HEAD" ] || die "detached HEAD; check out the target branch first" [ "$SOURCE" != "$TARGET" ] || die "source and target are the same branch ('$TARGET')" require_clean_worktree # Use the COMMON git dir, not `--git-dir`: in a linked worktree the latter is # the per-worktree gitdir (.git/worktrees/), but git reads info/attributes # only from the shared .git, so a driver mapping written to the per-worktree dir # would be silently ignored and the merge would fall back to the default driver. GIT_COMMON_DIR="$(cd "$(git rev-parse --git-common-dir)" && pwd)" INFO_ATTR="$GIT_COMMON_DIR/info/attributes" DRIVER="$SCRIPT_DIR/sync-merge-driver.sh" [ -x "$DRIVER" ] || die "merge driver not executable: $DRIVER" # ---- set up the local, temporary merge driver ----------------------------- ATTR_BACKUP="" cleanup() { # Restore .git/info/attributes and drop the temporary driver config, # regardless of how the merge ended (clean, conflicted, or aborted). if [ -n "$ATTR_BACKUP" ]; then mv -f "$ATTR_BACKUP" "$INFO_ATTR" else rm -f "$INFO_ATTR" fi git config --unset-all merge.rocqsync.name 2>/dev/null || true git config --unset-all merge.rocqsync.driver 2>/dev/null || true } trap cleanup EXIT if [ -e "$INFO_ATTR" ]; then ATTR_BACKUP="$(mktemp "${INFO_ATTR}.bak.XXXXXX")" cp "$INFO_ATTR" "$ATTR_BACKUP" fi mkdir -p "$(dirname "$INFO_ATTR")" cat > "$INFO_ATTR" <<'ATTRS' # Temporary: installed by scripts/sync-branch.sh, removed on exit. # The driver derives every substitution from OUR side and is a no-op on files # with no version tokens (and on binary files), so it is safe for all files. * merge=rocqsync ATTRS git config merge.rocqsync.name "CoqHammer version-token aware merge" git config merge.rocqsync.driver "$DRIVER %O %A %B %L %P" # rerere makes any genuine resolution reusable on the next sync. It must be on # *during* the merge to capture the conflict preimages, so enable it now, but # remember its previous state: if the merge turns out clean there is nothing to # record and we restore rerere to how we found it (see below). Only when real # conflicts remain -- which you resolve by hand after this script exits, while # rerere records the resolution -- does it need to stay on, and there we tell # you it was enabled and how to switch it off. RERERE_WAS="$(git config --get rerere.enabled 2>/dev/null || true)" git config rerere.enabled true restore_rerere() { # Put rerere.enabled back the way we found it (clean-merge path only). if [ "$RERERE_WAS" = "true" ]; then : # was already on; leave it elif [ -n "$RERERE_WAS" ]; then git config rerere.enabled "$RERERE_WAS" # restore an explicit value else git config --unset rerere.enabled 2>/dev/null || true # was unset; unset fi } # ---- do the merge --------------------------------------------------------- info "merging '$SOURCE' into current branch '$TARGET'" set +e git merge --no-edit -m "Merge ${SOURCE} into ${TARGET}" "$SOURCE" MERGE_RC=$? set -e if [ "$MERGE_RC" -eq 0 ]; then # Clean merge: no conflicts were recorded, so there is no reason to leave # rerere enabled repo-wide -- put it back the way we found it. restore_rerere info "merge completed cleanly on '$TARGET'." info "review it (git show / git log) and push when satisfied:" info " git push origin $TARGET" exit 0 fi # Non-trivial conflicts remain: leave the merge in progress on the current # branch for the user. echo >&2 info "the version-token differences were resolved automatically, but real" info "conflicts remain on '$TARGET'. Resolve them, then commit the merge:" echo >&2 git diff --name-only --diff-filter=U | sed 's/^/ /' >&2 echo >&2 info " git add && git commit --no-edit" info "or abort with: git merge --abort (leaves you on '$TARGET')" echo >&2 info "rerere will remember how you resolve these and reapply it on the next" info "sync. Because conflicts remain it is left enabled, and (if it was not" info "already on) it now applies to ALL merges in this repository. Switch off" info "once you no longer want that:" info " git config --unset rerere.enabled # cached resolutions kept" info " git rerere clear # also drop what it learned" exit "$MERGE_RC" coqhammer-1.3.3-9.2/scripts/sync-merge-driver.sh000077500000000000000000000177641522316141700214110ustar00rootroot00000000000000#!/usr/bin/env bash # # sync-merge-driver.sh %O %A %B %L %P # # Custom git merge driver used by `just sync` (see sync-branch.sh). # # It is registered locally (in .git/config + .git/info/attributes) for every # file in the merge. Its job is to make the *trivial* per-branch differences -- # the Rocq-version-flavored tokens that identify which Rocq a CoqHammer branch # targets -- disappear from the 3-way merge so that only genuine changes remain # (and genuinely conflict). # # git invokes a merge driver with: # %O path to a temp file holding the merge-base version # %A path to a temp file holding *our* version -- the branch being merged # INTO (the branch you are on) -- also the output file # %B path to a temp file holding *their* version -- the branch being merged # %L conflict marker size # %P the real pathname of the file being merged # # The driver is direction-agnostic: for each token class it reads the value # from OUR side (%A, the current branch) and rewrites the base (%O) and theirs # (%B) to that same value before handing off to `git merge-file`. Because the # flavored tokens then match on all three sides they never conflict, while real # edits to those same lines still merge cleanly. Every rule derives its value # from the file being merged and is a no-op on files that lack the token, so # the driver is safe to attach to all files. The exit status of # `git merge-file` (0 = clean, >0 = number of conflicts) is propagated so real # conflicts still surface for manual resolution. set -euo pipefail O="$1"; A="$2"; B="$3"; L="${4:-7}"; P="${5:-}" # Sentinel that stands in for the standalone prover-name word ("Coq"/"Rocq") # while merging Markdown; see rule 9. Chosen so it cannot occur in real text. CQ_SENTINEL='@@RocqOrCoq@@' # Documentation files get the prose Coq<->Rocq rule (9); left 0 for the early # binary-file merge below, set for real once we know the file is text. DOC=0 # OUR side's prevailing prover word, used only to turn back any sentinel that # survives into genuinely-new text taken from theirs (computed below). ADOMWORD='Rocq' do_merge() { set +e git merge-file --marker-size="$L" "$A" "$O" "$B" local rc=$? # Restore any sentinel that reached the output (only possible on lines that # came verbatim from theirs, or inside an unresolved conflict) to OUR word. [ "$DOC" = 1 ] && sed -i "s/$CQ_SENTINEL/$ADOMWORD/g" "$A" exit $rc } # Never run text substitutions on binary content (a file is binary if stripping # NUL bytes changes it). is_binary() { ! LC_ALL=C tr -d '\000' < "$1" | cmp -s - "$1"; } if is_binary "$A" || is_binary "$B" || is_binary "$O"; then do_merge fi # The prose Coq<->Rocq rule (9) applies to Markdown only: standalone "Coq" and # "Rocq" pervade source and .v files (module paths, "From Coq Require", ...) # where a blanket rewrite would silently absorb genuine code differences. case "$P" in *.md|*.markdown) DOC=1 ;; esac # OUR prevailing prover word: whichever of the standalone words dominates on our # side (master mixes both -- "Rocq master" but "versions of Coq"). if [ "$DOC" = 1 ]; then arocq="$(grep -oE '\bRocq\b' "$A" | wc -l)" acoq="$(grep -oE '\bCoq\b' "$A" | wc -l)" [ "$acoq" -gt "$arocq" ] && ADOMWORD='Coq' || ADOMWORD='Rocq' fi # ---- derive OUR (%A) value for each token class --------------------------- # # Each value is read from the file currently being merged, so the rules are # self-scoping: build-metadata files yield the metadata tokens, README/CI # files yield the documentation/CI tokens, and every other file yields none. # Build metadata (*.opam, dune, META.*): # 1. runtime library prefix: coq-core.* (release Rocq) vs rocq-runtime.* # (unstable Rocq); only the prefix differs, the suffix is identical. if grep -q 'rocq-runtime' "$A"; then PREFIX='rocq-runtime' elif grep -q 'coq-core' "$A"; then PREFIX='coq-core' else PREFIX='' fi # 2. opam version + maintainer: pure per-branch identity metadata (dev branch # ".dev"/dev-maintainer vs release branch "+"/release- # maintainer), so OUR value always wins. AVER="$(sed -n 's/^version: "\(.*\)"/\1/p' "$A" | head -n1)" AMAINT="$(sed -n 's/^maintainer: "\(.*\)"/\1/p' "$A" | head -n1)" # 3. Rocq dependency line(s): the ported rocq-* branches carry a two-line # '"rocq-core" {...}' + '"rocq-stdlib" {...}' block, master a single # '"rocq-stdlib" {= "dev"}', an older branch a single '"coq" {>= ...}'; # all pure per-branch metadata, so OUR whole block wins. Capture every Rocq # dependency line from OUR side (they are adjacent in `depends`) so the full # block -- not just the first line -- is substituted into base/theirs. ADEP_RE='^[[:space:]]*"(rocq-core|rocq-stdlib|coq)"[[:space:]]*[{]' ADEP_FILE="$(mktemp)" trap 'rm -f "$ADEP_FILE"' EXIT grep -E "$ADEP_RE" "$A" > "$ADEP_FILE" || true # Documentation / CI (README.md, .github/workflows/*, ...): # 4. workflow-status badge branch: ...badge.svg?branch= ABADGE="$(sed -n -E 's/.*[?&]branch=([A-Za-z0-9._-]+).*/\1/p' "$A" | head -n1)" # 5. docker image tag: rocq/rocq-prover: (tag is "dev" on master) ATAG="$(sed -n -E 's#.*rocq/rocq-prover:([A-Za-z0-9._-]+).*#\1#p' "$A" | head -n1)" # 6. prose / link label: "