├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── lean_action_ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── NOTICE ├── README.md ├── RustLeanModels.lean ├── RustLeanModels ├── Basic.lean ├── Iterator.lean ├── Iterator.md ├── ProofExample.lean ├── ProofTutorial.lean ├── RustString.lean ├── RustString.md └── UTF8Str.lean ├── lake-manifest.json ├── lakefile.toml └── lean-toolchain /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Copyright Kani Contributors 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | * @model-checking/kani-devs @BrunoDutertre 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Please add a description of your PR. 2 | 3 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. 4 | -------------------------------------------------------------------------------- /.github/workflows/lean_action_ci.yml: -------------------------------------------------------------------------------- 1 | name: Lean Action CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: leanprover/lean-action@v1 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.lake 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## rust-lean-models 7 | 8 | Lean models of various functions and data types from Rust libraries to facilitate Lean-based verification of Rust programs. 9 | 10 | 11 | ## Description 12 | This library is intended to be used for verifying Rust programs via Lean. It 13 | defines equivalent Lean versions of functions from the Rust standard library. 14 | 15 | The Lean definitions are based on the description of the Rust functions, which is published at https://doc.rust-lang.org/std. 16 | 17 | The library includes: 18 | - Definitions of Lean functions equivalent to those from the Rust standard library 19 | - Proofs that these definitions are consistent with the description of the Rust functions 20 | - Supporting lemmas and theorems for proof construction 21 | 22 | ## Installation 23 | 24 | - Add the following lines to your `lakefile.lean`: 25 | 26 | ` require «rust-lean-models» from git "https://github.com/model-checking/rust-lean-models" ` 27 | 28 | - Change the lean version in your `lean-toolchain` to the one in 29 | [lean-toolchain](https://github.com/model-checking/rust-lean-models/blob/main/lean-toolchain) 30 | 31 | - Run `lake update` in the terminal. 32 | 33 | - Import the packages and open the namespaces in your Lean files 34 | (see [ProofTutorial.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofTutorial.lean)) 35 | 36 | ```lean 37 | import RustLeanModels.Basic 38 | import RustLeanModels.RustString 39 | open RustString 40 | open Iterator 41 | ``` 42 | 43 | ## Usage 44 | ### Translating a Rust program 45 | 46 | Replace the Rust built-in functions that are used in your code with the 47 | corresponding Lean function in this library. 48 | 49 | Tables in [RustString.md](RustLeanModels/RustString.md) 50 | and [Iterators.md](RustLeanModels/Iterator.md) give the mapping from Rust types 51 | and functions to their Lean equivalents. 52 | 53 | ### Proof Tutorial 54 | 55 | We demonstrate the applications of the library and some proof techniques 56 | for string programs in 57 | [ProofTutorial.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofTutorial.lean). 58 | This tutorial shows the correctness of two simple programs that compute the longest common prefix 59 | and longest common substring of the two strings. 60 | More examples can be found in 61 | [ProofExample.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofExample.lean). 62 | 63 | ## Details 64 | 65 | ### Recursive function definitions 66 | 67 | For each Rust function, we provide a recursive Lean function. Implementing 68 | the equivalent functions in Lean recursively enables users to construct 69 | induction proofs conveniently. The Lean function has the same name as the Rust original, 70 | except when the Rust name clashes with a Lean keyword. In case of a clash, a Rust function `func_name` 71 | is renamed to `rust_func_name` in Lean. 72 | 73 | Implementing a function recursively may requires some extra parameters. 74 | In those cases, we first define an auxiliary function (name: `func_name_aux`) which is defined 75 | recursively with the extra parameters, then we define the function `func_name` based on the auxiliary function 76 | with initial values for the parameters. 77 | For example, `char_indices` is defined based on `char_indices_aux` as 78 | ```lean 79 | def char_indices (s: Str) := char_indices_aux s 0 80 | ``` 81 | 82 | For Rust functions that use the Rust `Pattern` type, we implement two recursive sub-functions 83 | (name: `func_name_char_filter` and `func_name_substring`) that replace the `Pattern` type 84 | in the input with either a char filter (of type`Char → Bool`) or a string (of type `List Char`). 85 | Then we define the function `func_name` based on these two sub-functions by matching the 86 | input of `Pattern` type. For example, `split` is defined by two sub-functions 87 | `split_char_filter` and `split_substring` as: 88 | 89 | ```lean 90 | def split (s: Str) (p: Pattern) := match p with 91 | | Pattern.SingleChar c => split_char_filter s (fun x => x = c) 92 | | Pattern.ListChar l => split_char_filter s (fun x => x ∈ l) 93 | | Pattern.FilterFunction f => split_char_filter s f 94 | | Pattern.WholeString s1 => split_substring s s1 95 | ``` 96 | 97 | All recursive implementations are proven to be "correct" in the sense that 98 | they are consistent with the descriptions of the Rust versions (see below for more details). 99 | 100 | ## Correctness Proofs 101 | 102 | ### For functions that return `Bool` or can be defined based on other functions that have already been proven correct 103 | 104 | - First, we provide a variant Lean definition of the Rust function that we call the definitional 105 | version (with name `func_name_def`). This version is intended to match the documented description 106 | of the Rust function. Ideally, the definitional version should not contain recursion, except in some trivial cases, 107 | but it can make use of the recursive versions of other functions that have been previously proven correct. 108 | 109 | - Then, we state and prove a theorem that shows that the recursive and definitional versions of Rust 110 | function `func_name` are equivalent. This equivalence theorem is called `func_name_EQ` and 111 | it has type `func_name = func_name_def`. 112 | The theorem ensures that the function is implemented correctly 113 | and allows the two versions to be used interchangeably. 114 | In some cases, constructing a non-induction proof using the definitional version is more convenient. 115 | 116 | - For example, the function `is_char_boundary` has a definitional version: 117 | 118 | ```lean 119 | def is_char_boundary_def (s: Str) (i: Nat) := i ∈ ListCharPos s ∨ i = byteSize s 120 | ``` 121 | 122 | a recursive definition: 123 | 124 | ```lean 125 | def is_char_boundary (s: Str) (i: Nat) := match s with 126 | | [] => i == 0 127 | | h::t => if i = 0 then true else 128 | if i < Char.utf8Size h then false else is_char_boundary t (i - Char.utf8Size h) 129 | ``` 130 | 131 | and an equivalence theorem 132 | 133 | ```lean 134 | theorem is_char_boundary_EQ : is_char_boundary s p = is_char_boundary_def s p 135 | ``` 136 | 137 | When the description of a Rust function cannot be efficiently expressed in Lean (requires recursions, or is unintuitive), 138 | we can: 139 | - Define the definitional version (similar to Case 1) based on a recursive trivial function, then prove the equivalence theorem. 140 | For example, the `byteSize_def` function is based on the function `sum_list_Nat` 141 | that computes the sum of a list of natural numbers: 142 | 143 | ```lean 144 | def sum_list_Nat (l : List Nat) := List.foldl Nat.add 0 l` 145 | def byteSize_def (s : List Char) : Nat := sum_list_Nat (List.map (fun x: Char => Char.utf8Size x) s) 146 | ``` 147 | 148 | - Define and prove the correctness of one/some subordinate function(s), 149 | then define the definitional version based on them. 150 | For example, to define `split_inclusive_char_filter_def`, we first define and prove the correctness of two functions: 151 | - `list_char_filter_charIndex (s: Str) (f: Char → Bool)`: returns the list of positions of characters in `s` satisfying the filter `f` 152 | 153 | - `split_at_charIndex_list (s: Str) (l: List Nat)`: splits the strings `s` at positions in `l` 154 | 155 | then we define `split_inclusive_char_filter_def` as follows: 156 | 157 | ```lean 158 | def split_inclusive_char_filter_def (s: Str) (f: Char → Bool) := 159 | split_at_charIndex_list s (List.map (fun x => x+1) (list_char_filter_charIndex s f)) 160 | ``` 161 | 162 | ### When the Rust documentation describes properties of the return value 163 | 164 | We state and prove a soundness theorem for the function with 165 | name: `func_name_sound` and type: `x = func_name input1 input2 ... ↔ properties of x`. 166 | 167 | For example, the soundness theorem for the function `floor_char_boundary` defined as: 168 | 169 | ```lean 170 | def floor_char_boundary_aux (s: Str) (i: Nat) (k: Nat): Nat := match s with 171 | | [] => k 172 | | h::t => if i < Char.utf8Size h then k else floor_char_boundary_aux t (i - Char.utf8Size h) (k + Char.utf8Size h) 173 | 174 | def floor_char_boundary (s: Str) (i: Nat) := floor_char_boundary_aux s i 0 175 | ``` 176 | 177 | is 178 | 179 | ```lean 180 | theorem floor_char_boundary_sound: flp = floor_char_boundary s p 181 | ↔ (is_char_boundary s flp) ∧ (flp ≤ p) ∧ (∀ k, ((is_char_boundary s k) ∧ (k ≤ p)) → k ≤ flp ) 182 | ``` 183 | 184 | - The soundness theorem should cover all the properties in the Rust documentation. 185 | - We make some minor/trivial adjustments when needed to express the properties in Lean. 186 | - The modus ponens (→) direction of the theorem is enough to ensure the correctness of the recursive version 187 | if the properties in the right-hand-side ensure that the function in the left-hand-side is deterministic. 188 | - The modus ponens reverse (←) direction ensures that we stated enough properties in right-hand-side such that 189 | it can be satisfied by only one function. 190 | 191 | If the function returns an option, we separately state and prove two soundness theorems for the two cases 192 | of the return value: `func_name_none_sound` and `func_name_some_sound`. For example: 193 | 194 | ```lean 195 | theorem split_at_none_sound : split_at s p = none ↔ ¬ ∃ s1, List.IsPrefix s1 s ∧ byteSize s1 = p 196 | theorem split_at_some_sound : split_at s p = some (s1, s2) ↔ byteSize s1 = p ∧ s = s1 ++ s2 197 | ``` 198 | 199 | For functions involving the `Pattern` type, we separately state and prove two equivalent/soundness 200 | theorems for the two sub-functions discussed previously (`func_name_char_filter_EQ` and `func_name_substring_EQ`) 201 | or (`func_name_char_filter_sound` and `func_name_substring_sound`). For example: 202 | 203 | ```lean 204 | theorem contains_char_filter_EQ : contains_char_filter s f = contains_char_filter_def s f 205 | theorem contains_substring_EQ : contains_substring s p = contains_substring_def s p 206 | ``` 207 | 208 | ## Security 209 | 210 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 211 | 212 | ## License 213 | 214 | This project is licensed under the Apache-2.0 License. 215 | -------------------------------------------------------------------------------- /RustLeanModels.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | -- This module serves as the root of the `RustLeanModels` library. 4 | -- Import modules here that should be built as part of the library. 5 | import «RustLeanModels».Basic 6 | -------------------------------------------------------------------------------- /RustLeanModels/Basic.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | open String 4 | open List 5 | open Nat 6 | 7 | -- library Defs 8 | abbrev Str := List Char 9 | 10 | --LOGIC 11 | 12 | theorem exists_minima_prop_nat {p: Nat → Prop} : (∃ x , p x) → ∃ m, (p m) ∧ (∀ n, p n → m ≤ n) :=by 13 | intro ⟨ n, gn ⟩ 14 | induction n using Nat.strongInductionOn 15 | rename_i n ind 16 | by_cases gm : (∀ m < n, ¬ p m ) 17 | · exists n ; simp only [gn, true_and]; intro x gx ; 18 | have gm := gm x 19 | simp only [gx, not_true_eq_false, imp_false, Nat.not_lt] at gm 20 | exact gm 21 | · simp only [Classical.not_forall, not_imp, Classical.not_not] at gm 22 | obtain ⟨m,gm⟩:=gm 23 | simp only [exists_prop, true_implies] at gm 24 | have ⟨ x, gx ⟩ := ind m gm.left gm.right 25 | exists x 26 | 27 | theorem imp_eq_not_or: (p → q) ↔ (¬ p ∨ q) :=by 28 | constructor 29 | · intro g; by_cases p <;> simp_all 30 | · intro g; cases g <;> simp_all 31 | 32 | def unique {α : Type} (p: α → Prop) : Prop := ∀ x1 x2, p x1 → p x2 → x1 = x2 33 | 34 | theorem unique_minima {p: Nat → Prop}: unique (fun x => p x ∧ (∀ y, p y → y ≥ x)) :=by 35 | unfold unique; intro x y g1 g2 36 | have := g1.right y g2.left 37 | have := g2.right x g1.left 38 | omega 39 | 40 | theorem unique_maxima {p: Nat → Prop}: unique (fun x => p x ∧ (∀ y, p y → y ≤ x)) :=by 41 | unfold unique; intro x y g1 g2 42 | have := g1.right y g2.left 43 | have := g2.right x g1.left 44 | omega 45 | 46 | theorem Eq_iff_of_unique_and_mp : unique p → (∀ y, (x = y) → p y) → (∀ y, ((x = y) ↔ p y)) := by 47 | intro g1 g2 y; constructor; exact g2 y; intro g3 48 | have g2:= g2 x; simp only [true_implies] at g2 49 | unfold unique at g1; exact g1 x y g2 g3 50 | 51 | @[simp] 52 | theorem not_sym : ¬ x = y → ¬ y = x := by intro g h; simp_all 53 | 54 | theorem exists_EQ {α: Type} {p q : α → Prop}: (∀ x, p x ↔ q x) → ((∃ y, p y) ↔ (∃ y, q y)) :=by 55 | intro g 56 | simp_all 57 | 58 | theorem not_in_forall_not_eq {s: List α} (h: ∀ x ∈ s, a ≠ x) : a ∉ s :=by 59 | intro g; have := h a g; contradiction 60 | 61 | -- NAT 62 | theorem cases_max : x = Nat.max m n → (x=m) ∨ (x=n) :=by 63 | rw [Nat.max, Nat.max_def] 64 | split <;> simp_all 65 | 66 | theorem max_add_right : Nat.max a (b+c) ≤ c + Nat.max a b :=by 67 | simp only [Nat.max, Nat.max_def] 68 | split <;> split <;> omega 69 | 70 | theorem le_max_iff : a ≤ Nat.max b c ↔ a ≤ b ∨ a ≤ c := by 71 | rw [Nat.max, Nat.max_def] 72 | split <;> omega 73 | 74 | theorem max_le_of_right_le: n ≤ k → Nat.max m n ≤ Nat.max m k := by 75 | intro g 76 | simp only [Nat.max, Nat.max_def] 77 | split <;> split <;> omega 78 | 79 | theorem map_add_sub {l: List Nat}: List.map (fun x => x - c) (List.map (fun x => x + c) l) = l := by 80 | simp only [map_map] 81 | have : ((fun x => x - c) ∘ fun x => x + c) = (fun x => x) :=by 82 | ext x; simp only [Function.comp_apply, Nat.add_sub_cancel_right] 83 | rw[this]; simp only [map_id'] 84 | 85 | theorem map_sub_add {l: List Nat} (g: ∀ x ∈ l, x ≥ c): List.map (fun x => x + c) (List.map (fun x => x - c) l) = l := by 86 | induction l 87 | case nil => simp 88 | case cons x tail ih => 89 | simp at * 90 | constructor 91 | . omega 92 | . apply ih 93 | exact g.right 94 | 95 | theorem map_add_comm {l: List Nat} : List.map (fun x => x + a) (List.map (fun x => x + b) l) = List.map (fun x => x + b) (List.map (fun x => x + a) l) :=by 96 | simp only [map_map, map_inj_left, Function.comp_apply] 97 | omega 98 | 99 | theorem map_sub_comm : List.map (fun x: Nat=> x-a) (List.map (fun x=> x-b) l) = List.map (fun x=> x-b) (List.map (fun x=> x-a) l) :=by 100 | simp only [map_map, map_inj_left, Function.comp_apply] 101 | omega 102 | 103 | def list_nat_zero_to_aux (n:Nat) (i: Nat): List Nat := match n with 104 | | zero => [] 105 | | succ t => i:: list_nat_zero_to_aux t (i+1) 106 | 107 | def list_nat_zero_to (n:Nat):= list_nat_zero_to_aux n 0 108 | 109 | def monotone (l: List Nat) := match l with 110 | |[] => true 111 | |h::t => (∀ m ∈ t, h ≤ m) ∧ monotone t 112 | 113 | theorem map_sub_monotone: monotone l → monotone (List.map (fun x => x -k ) l) :=by 114 | induction l 115 | case nil => simp 116 | case cons y l' ih => 117 | simp [monotone] 118 | intro le mon 119 | simp only [ih mon, and_true] 120 | intro a ha 121 | exact Nat.sub_le_sub_right (le a ha) k 122 | 123 | theorem map_add_monotone: monotone l → monotone (List.map (fun x => x + k ) l) :=by 124 | induction l 125 | case nil => 126 | unfold monotone 127 | simp only [map_nil]; simp only [imp_self] 128 | case cons => 129 | rename_i h t ind 130 | unfold monotone 131 | simp only [Bool.decide_and, Bool.decide_eq_true, Bool.and_eq_true, decide_eq_true_eq, map_cons, 132 | mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂] 133 | intro g g1; simp only [ind g1, and_true] 134 | intro a ga ; have ga := g a ga; omega 135 | 136 | theorem list_nat_zero_to_aux_para1_sub : list_nat_zero_to_aux n a = List.map (fun x => x - b) (list_nat_zero_to_aux n (a+b)):=by 137 | induction n generalizing a b 138 | case zero => 139 | simp [list_nat_zero_to_aux] 140 | case succ n' ih => 141 | simp only [list_nat_zero_to_aux, map_cons, Nat.add_sub_cancel_right, cons.injEq, true_and] 142 | rw [ih (b := b)] 143 | have : a + b + 1 = (a + 1) + b:= by omega 144 | rw [this] 145 | 146 | theorem list_nat_zero_to_aux_para1_add : List.map (fun x => x + b) (list_nat_zero_to_aux n a) = (list_nat_zero_to_aux n (a+b)):=by 147 | induction n generalizing a b 148 | case zero => simp only [list_nat_zero_to_aux, map_nil] 149 | case succ n ind => 150 | simp only [list_nat_zero_to_aux, map_cons, Nat.add_sub_cancel_right, cons.injEq, true_and] 151 | have : a + b + 1 = (a + 1) + b := by omega 152 | rw[this]; exact ind 153 | 154 | theorem list_nat_zero_to_monotone : monotone (list_nat_zero_to n) :=by 155 | induction n 156 | case zero => simp only [list_nat_zero_to, list_nat_zero_to_aux, monotone] 157 | case succ n ind => 158 | simp only [list_nat_zero_to, list_nat_zero_to_aux, Nat.zero_add] 159 | unfold monotone; simp only [Nat.zero_le, implies_true, true_and, Bool.decide_eq_true] 160 | have i:=@list_nat_zero_to_aux_para1_add 1 n 0; simp only [Nat.zero_add] at i; rw[← i, ← list_nat_zero_to] 161 | apply map_add_monotone ind 162 | 163 | theorem list_nat_zero_to_le : ∀ m ∈ (list_nat_zero_to n), m ≤ n :=by 164 | induction n 165 | case zero => simp only [list_nat_zero_to, list_nat_zero_to_aux, not_mem_nil, false_implies, implies_true] 166 | case succ n ind => 167 | simp only [list_nat_zero_to, list_nat_zero_to_aux, Nat.zero_add, mem_cons, forall_eq_or_imp, 168 | Nat.zero_le, true_and] 169 | have i:=@list_nat_zero_to_aux_para1_add 1 n 0; simp only [Nat.zero_add] at i; rw[← i, ← list_nat_zero_to] 170 | simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂, Nat.add_le_add_iff_right] 171 | exact ind 172 | 173 | theorem list_nat_zero_to_aux_ge : x ∈ list_nat_zero_to_aux n i → x ≥ i :=by 174 | induction n generalizing i 175 | case zero => simp only [list_nat_zero_to_aux, not_mem_nil, ge_iff_le, false_implies] 176 | case succ n ind => 177 | simp only [list_nat_zero_to_aux, mem_cons, ge_iff_le] 178 | intro g; cases g; omega; rename_i g; have ind:=ind g; omega 179 | 180 | theorem list_nat_zero_to_aux_first_mem : list_nat_zero_to_aux n i = h::t → h = i :=by 181 | cases n 182 | simp only [list_nat_zero_to_aux, not_false_eq_true, not_sym, false_implies] 183 | simp only [list_nat_zero_to_aux, cons.injEq, and_imp] 184 | intro g _; omega 185 | 186 | --LIST 187 | 188 | theorem exist_last_mem : l.length > 0 → ∃ s c, l = s ++ [c] :=by 189 | generalize gr: reverse l = r 190 | cases r; simp only [reverse_eq_nil_iff] at gr; simp only [gr, length_nil, gt_iff_lt, 191 | nil_eq_append, and_false, not_false_eq_true, not_sym, exists_false, 192 | exists_const] 193 | simp only [Nat.lt_irrefl, imp_self] 194 | intro _g; rename_i h t ; exists reverse t, h; 195 | have: reverse (reverse l) = reverse t ++ [h] := by simp only [gr, reverse_cons] 196 | simp only [reverse_reverse] at this; exact this 197 | 198 | theorem length_pos_from_not_nil : s ≠ [] ↔ s.length > 0 := by 199 | simp [length_pos] 200 | 201 | theorem getLast?_cons_cons : (h1::h2::t).getLast? = (h2::t).getLast? := by 202 | rw(config:={occs:= .pos [1]}) [getLast?] ; 203 | simp only; unfold getLast getLast?; simp only 204 | 205 | theorem getLast?_cons_tail_ne_nil: t ≠ [] → (h::t).getLast? = t.getLast? :=by 206 | cases t; simp only [ne_eq, not_true_eq_false, dropLast_single, dropLast_nil, not_false_eq_true, 207 | not_sym] 208 | simp only [getLast?_singleton, getLast?_nil, not_false_eq_true, not_sym, imp_self] 209 | rw[List.getLast?_cons, List.getLast?_cons] 210 | simp only [getLastD_cons] 211 | simp only [ne_eq, not_false_eq_true, not_sym, imp_self] 212 | 213 | theorem dropLast_append_getLast : s = s.dropLast ++ [(s.getLast a)] :=by 214 | induction s 215 | case nil => contradiction 216 | case cons h t ind => 217 | cases t; simp only [dropLast_single, getLast_singleton, List.nil_append] 218 | rename_i ht tt; simp only [dropLast_cons₂, cons_append, cons.injEq, true_and] 219 | have : ht :: tt ≠ [] :=by simp only [ne_eq, not_false_eq_true, not_sym] 220 | apply ind 221 | 222 | theorem reverse_iif {l1 l2 : List α}: l1 = l2 ↔ l1.reverse = l2.reverse :=by 223 | constructor; intro g; rw[g] 224 | intro g; rw[← @reverse_reverse _ l1, ← @reverse_reverse _ l2, g] 225 | 226 | theorem reverse_change_side {l1 l2 : List α}: l1.reverse = l2 ↔ l1 = l2.reverse :=by 227 | rw[reverse_iif, reverse_reverse]; 228 | 229 | 230 | /- 231 | PREFIX theorems 232 | -/ 233 | 234 | 235 | @[simp] 236 | theorem self_prefix : List.IsPrefix s s :=by 237 | unfold List.IsPrefix; exists [] ; simp only [List.append_nil] 238 | 239 | @[simp] 240 | theorem nil_prefix : List.IsPrefix [] s :=by apply List.nil_prefix 241 | 242 | @[simp] 243 | theorem head_prefix : List.IsPrefix [h] (h::t) :=by 244 | unfold List.IsPrefix; exists t 245 | 246 | theorem prefix_length_le (h: List.IsPrefix p s) : p.length ≤ s.length :=by 247 | unfold List.IsPrefix at h; 248 | obtain ⟨t, ht⟩ := h ; 249 | have i: s.length = p.length + t.length := by rw [← ht]; apply List.length_append 250 | omega 251 | 252 | theorem prefix_eq_self (h: List.IsPrefix p s) (hl: p.length = s.length) : p = s :=by 253 | unfold List.IsPrefix at h 254 | obtain ⟨t, ht⟩ := h 255 | have : p.length + t.length = s.length := by rw[← ht] ; apply Eq.symm; apply List.length_append 256 | simp_all only 257 | have := Nat.add_left_cancel (k := 0) this 258 | have : t =[] := by apply List.eq_nil_of_length_eq_zero this 259 | rw[this] at ht; simp_all only [List.append_nil, length_nil] 260 | 261 | theorem prefix_eq_of_length_eq (h1: List.IsPrefix p1 s) (h2: List.IsPrefix p2 s) 262 | (hl: p1.length = p2.length) : p1 = p2 :=by 263 | have i1: List.IsPrefix p1 p2 := by apply List.prefix_of_prefix_length_le h1 h2 ; omega 264 | apply prefix_eq_self i1 hl 265 | 266 | theorem prefix_eq_take (h: List.IsPrefix p s) : s.take p.length = p := by 267 | have e1: List.IsPrefix (s.take p.length) s := by apply List.take_prefix 268 | have e2: (s.take p.length).length = min p.length s.length := by apply List.length_take 269 | have e3 := prefix_length_le h 270 | have e4: min p.length s.length = p.length :=by simp only [ge_iff_le, e3, Nat.min_eq_left] 271 | rw[e4] at e2 272 | apply prefix_eq_of_length_eq e1 h e2 273 | 274 | @[simp] 275 | theorem prefix_cons {α : Type} {hp hs : α} {tp ts : List α} : 276 | List.IsPrefix (hp::tp) (hs::ts) → (hp = hs) ∧ (List.IsPrefix tp ts) :=by 277 | intro h 278 | unfold List.IsPrefix at h 279 | obtain ⟨q,hq⟩ := h 280 | simp only [cons_append, cons.injEq] at hq 281 | simp only [hq, true_and] ; unfold List.IsPrefix; exists q ; simp only [hq] 282 | 283 | 284 | theorem prefix_cons_mpr {u: Type} {tp ts: List u} (h: List.IsPrefix tp ts) (a:u) 285 | : (List.IsPrefix (a::tp) (a::ts)) :=by 286 | unfold List.IsPrefix at h 287 | obtain ⟨q,hq⟩ := h 288 | unfold List.IsPrefix; exists q; simp only [cons_append, hq] 289 | 290 | theorem prefix_map {p s: List Char} (g: List.IsPrefix p s) (f: Char → Nat): List.IsPrefix (List.map f p) (List.map f s) :=by 291 | induction s generalizing p 292 | case nil => simp only [prefix_nil] at g; simp only [g, map_nil, self_prefix] 293 | case cons hs ts ind => 294 | cases p; simp only [map_nil, map_cons, _root_.nil_prefix] 295 | rename_i hp tp 296 | have g := prefix_cons g 297 | simp only [map_cons, g]; 298 | have := ind (g.right) 299 | apply (prefix_cons_mpr this (f hs)) 300 | 301 | theorem prefix_trans (g1: List.IsPrefix p s) (g2: List.IsPrefix s t) : List.IsPrefix p t :=by 302 | unfold List.IsPrefix at g1 g2 303 | unfold List.IsPrefix 304 | obtain ⟨q,hq⟩ := g1 305 | obtain ⟨r,hr⟩ := g2 306 | exists q++r; 307 | rw [← List.append_assoc]; rw[hq, hr] 308 | 309 | theorem prefix_mem (g: List.IsPrefix p s) (g2: m ∈ p) : m ∈ s := by 310 | unfold List.IsPrefix at g; obtain ⟨q,hq⟩ := g 311 | rw[← hq] ; apply List.mem_append_left q g2 312 | 313 | theorem prefix_append_or : List.IsPrefix (l1 ++ t1) (l2 ++ t2) 314 | → (List.IsPrefix l1 l2) ∨ (List.IsPrefix l2 l1) :=by 315 | intro h 316 | unfold List.IsPrefix at h 317 | obtain ⟨v, hv⟩ := h 318 | have p1: List.IsPrefix l1 (l2 ++ t2) := by 319 | unfold List.IsPrefix; exists t1 ++ v ; rw[← hv]; simp 320 | have p2: List.IsPrefix l2 (l2 ++ t2) :=by simp 321 | apply List.prefix_or_prefix_of_prefix p1 p2 322 | 323 | theorem IsPrefix_isPrefixOf_EQ {p : List Char} : List.isPrefixOf p l ↔ List.IsPrefix p l :=by 324 | constructor 325 | case mp => 326 | intro h 327 | generalize hi: List.length l = i 328 | induction i generalizing l p 329 | case zero => 330 | have i1: l = [] :=by apply List.eq_nil_of_length_eq_zero; exact hi 331 | have i2: p = [] :=by 332 | unfold List.isPrefixOf at h; 333 | split at h; simp only; contradiction; 334 | simp_all only [length_nil, zero_eq, not_false_eq_true, not_sym] 335 | simp_all only [isPrefixOf_nil_left, length_nil, zero_eq, prefix_nil] 336 | case succ n ind => 337 | unfold List.IsPrefix 338 | unfold List.isPrefixOf at h; split at h 339 | exists l; 340 | contradiction 341 | rename_i a ais b bis 342 | have e1: a = b :=by simp_all only [length_cons, succ.injEq, Bool.and_eq_true, beq_iff_eq] 343 | rw[e1] 344 | have p1: List.isPrefixOf ais bis = true :=by simp_all only [length_cons, succ.injEq, 345 | beq_self_eq_true, Bool.true_and] 346 | have p2: bis.length = n :=by simp_all only [length_cons, succ.injEq, beq_self_eq_true, 347 | Bool.and_self] 348 | have p3:= ind p1 p2 349 | unfold List.IsPrefix at p3 350 | obtain ⟨t, ht⟩ := p3 351 | exists t; simp_all only [length_cons, beq_self_eq_true, Bool.and_self, cons_append] 352 | case mpr => 353 | intro h 354 | generalize hi: List.length l = i 355 | induction i generalizing l p 356 | have i1: l = [] :=by apply List.eq_nil_of_length_eq_zero; exact hi 357 | have i2: p = [] :=by apply List.prefix_nil.mp; simp_all only [prefix_nil, length_nil, zero_eq] 358 | simp_all only [isPrefixOf_nil_left, length_nil, zero_eq, prefix_nil] 359 | rename_i n ind 360 | unfold List.IsPrefix at h 361 | unfold List.isPrefixOf ; split; simp only; 362 | simp_all only [imp_false, append_eq_nil, false_and, not_false_eq_true, not_sym, exists_const] 363 | rename_i a ais b bis 364 | have e1: a = b :=by 365 | simp_all only [length_cons, succ.injEq, cons_append, cons.injEq, exists_and_left] 366 | simp_all only [length_cons, succ.injEq, cons_append, cons.injEq, true_and, beq_self_eq_true, 367 | Bool.true_and] 368 | have p1: List.IsPrefix ais bis := h 369 | apply ind p1 hi 370 | 371 | theorem prefix_append_drop : List.IsPrefix p s → s = p ++ (s.drop p.length) := by 372 | intro g; have i:= prefix_eq_take g; 373 | rw(config:={occs:=.pos [1]})[← i]; simp only [take_append_drop] 374 | 375 | 376 | /- 377 | SUFFIX theorems 378 | -/ 379 | theorem IsSuffix_isSuffixOf_EQ {p : List Char}: List.isSuffixOf p l ↔ List.IsSuffix p l := by 380 | rw[List.isSuffixOf, IsPrefix_isPrefixOf_EQ] 381 | rw[← List.reverse_suffix]; simp only [reverse_reverse] 382 | 383 | theorem suffix_trans (g1: List.IsSuffix s2 s1) (g2: List.IsSuffix s1 s) : List.IsSuffix s2 s :=by 384 | unfold List.IsSuffix at * 385 | obtain⟨t1,g1⟩:=g1; obtain⟨t2,g2⟩:=g2; rw[← g1, ← List.append_assoc] at g2; exists t2++t1 386 | 387 | theorem suffix_eq_drop (g: List.IsSuffix p s) : s.drop (s.length - p.length) = p := by 388 | unfold List.IsSuffix at g ; obtain ⟨t,g⟩:= g 389 | have : t = s.take (t.length) := by simp only [← g, take_left] 390 | simp only [← g, List.length_append, Nat.add_sub_cancel_right, drop_left] 391 | 392 | theorem take_append_suffix (g: List.IsSuffix p s): s = List.take (s.length - p.length) s ++ p:= by 393 | rw (config:={occs:=.pos [2]}) [← suffix_eq_drop g] 394 | simp only [take_append_drop] 395 | 396 | 397 | /- 398 | ZIP and UNZIP 399 | -/ 400 | 401 | theorem unzip_s (gs: s= List.zip s1 s2): 402 | (s.unzip.2 = s2.take s.length) ∧ (s.unzip.1 = s1.take s.length) := by 403 | induction s generalizing s1 s2; 404 | case nil => simp only [unzip_nil, length_nil, take_zero, and_self] 405 | case cons h t ind => 406 | simp only [unzip_cons, length_cons] 407 | cases s1; cases s2 408 | simp only [zip_nil_right, not_false_eq_true, not_sym] at gs 409 | simp only [zip_nil_left, not_false_eq_true, not_sym] at gs 410 | cases s2 411 | simp only [zip_nil_right, not_false_eq_true, not_sym] at gs 412 | simp only [zip_cons_cons, cons.injEq] at gs 413 | simp_all only [length_zip, take_succ_cons, and_self] 414 | 415 | theorem unzip_eq_left_shorter (gs: s= List.zip s1 s2) (gl: s1.length ≤ s2.length): 416 | s.unzip.1 = s1 := by 417 | have z := (unzip_s gs).right 418 | simp_all only [length_zip, ge_iff_le, Nat.min_eq_left, take_length] 419 | 420 | theorem unzip_eq_right_shorter (gs: s= List.zip s1 s2) (gl: s2.length ≤ s1.length): 421 | s.unzip.2 = s2 := by 422 | have z := (unzip_s gs).left 423 | simp_all only [length_zip, ge_iff_le, Nat.min_eq_right, take_length] 424 | 425 | theorem nil_zip_left (g: List.zip [a] b = []) : b = [] :=by 426 | unfold List.zip at g 427 | have := List.zipWith_eq_nil_iff.mp g 428 | simp_all only [zipWith_eq_nil_iff, not_false_eq_true, not_sym, false_or, or_true] 429 | 430 | theorem nil_zip_right (h: List.zip b [a] = []) : b = [] :=by 431 | unfold List.zip at h 432 | have := List.zipWith_eq_nil_iff.mp h 433 | simp_all only [zipWith_eq_nil_iff, not_false_eq_true, not_sym, or_false] 434 | 435 | theorem take_n {α: Type} {head : α} {tail: List α}: 436 | List.take (succ n) (head :: tail) = head:: List.take n tail:=by 437 | unfold List.take; simp 438 | generalize ht : List.take n tail = te 439 | unfold List.take at ht; 440 | simp [ht] 441 | 442 | theorem zip_take_zip {α: Type} {β: Type} {a: List α} {b: List β}: 443 | (List.zip a b).take n = List.zip (a.take n) (b.take n) :=by 444 | generalize hl: (List.zip a b) = s 445 | induction s generalizing a b n 446 | case nil => 447 | unfold List.zip at hl 448 | have hl:= List.zipWith_eq_nil_iff.mp hl 449 | cases hl; 450 | simp_all only [zipWith_nil_left, take_nil, zip_nil_left] 451 | simp_all only [zipWith_nil_right, take_nil, zip_nil_right] 452 | case cons head tail ind => 453 | cases a; simp_all only [zip_nil_left, not_false_eq_true, not_sym] 454 | cases b; simp_all only [zip_nil_right, not_false_eq_true, not_sym] 455 | rename_i ha ta hb tb 456 | have tab: List.zip ta tb = tail := by simp_all only [zip_cons_cons, cons.injEq] 457 | cases n; simp only [zero_eq, take_zero, zip_nil_right] 458 | rename_i n 459 | rw[take_n, take_n, take_n] 460 | rw[zip_cons_cons] 461 | rw[zip_cons_cons] at hl 462 | have hab: head = (ha, hb) :=by simp_all only [cons.injEq, and_true] 463 | simp only [hab, cons.injEq, true_and] 464 | apply ind tab 465 | 466 | theorem unzip_take_eq_take_unzip : 467 | (List.unzip a).1.take n = (List.unzip (a.take n)).1 :=by 468 | induction a generalizing n 469 | case nil => simp only [unzip_nil, take_nil] 470 | case cons h t ind => 471 | simp_all only [unzip_cons] 472 | cases n; simp only [zero_eq, take_zero, unzip_nil] 473 | rename_i n; unfold List.take 474 | simp_all only [unzip_cons, cons.injEq, true_and] 475 | 476 | def option_eq (a: Option Char) (b: Char) : Bool := (a = some b) 477 | 478 | def option_not_eq (a: Option Char) (b: Char) : Bool := match a with 479 | | none => true 480 | | some s => s ≠ b 481 | 482 | theorem of_mem_unzip (g: m ∈ (List.unzip l).1) : ∃ n, (m,n) ∈ l := by 483 | induction l 484 | case nil => simp only [unzip_nil, List.not_mem_nil] at g 485 | case cons h t ind => 486 | simp only [unzip_cons, mem_cons] at g 487 | cases g; exists h.2; simp_all only [mem_cons, true_or] 488 | rename_i h; have ind:=ind h 489 | obtain ⟨n, hn⟩ := ind 490 | exists n; simp_all only [mem_cons, or_true] 491 | 492 | theorem unzip_zip_take_left : (List.unzip (List.zip a b)).1 = a.take (min a.length b.length) := by 493 | generalize hl: (List.zip a b) = s 494 | induction s generalizing a b 495 | case nil => 496 | simp_all only [unzip_nil] 497 | unfold List.zip at hl 498 | have hl:= List.zipWith_eq_nil_iff.mp hl 499 | cases hl; 500 | simp_all only [zipWith_nil_left, length_nil, ge_iff_le, Nat.zero_le, Nat.min_eq_left, take_nil] 501 | have: b.length = 0 :=by rename_i h; simp only [h, length_nil] 502 | simp only [this, ge_iff_le, Nat.zero_le, Nat.min_eq_right, take_zero] 503 | case cons head tail ind => 504 | cases a; simp only [zip_nil_left, not_false_eq_true, not_sym] at hl 505 | cases b; simp only [zip_nil_right, not_false_eq_true, not_sym] at hl 506 | rename_i ha ta hb tb 507 | have tab: tail = List.zip ta tb := by simp_all only [zip_cons_cons, cons.injEq] 508 | simp_all only [zip_cons_cons, cons.injEq, and_true, unzip_cons, length_cons] 509 | have tka: List.take (min (succ ta.length) (succ tb.length)) (ha :: ta) 510 | = ha::List.take (min ta.length tb.length) ta :=by 511 | unfold List.take 512 | have : min (succ ta.length) (succ tb.length) = succ (min ta.length tb.length) 513 | :=by apply succ_min_succ 514 | simp [this] 515 | generalize ht : List.take (min ta.length tb.length) ta = t 516 | unfold List.take at ht 517 | simp only [ht] 518 | rw[tka] 519 | have : head.1 = ha :=by rw [hl.symm] 520 | simp only [this, cons.injEq, true_and] 521 | apply ind; simp 522 | 523 | 524 | theorem unzip_zip_take_right: (List.unzip (List.zip a b)).2 = b.take (min a.length b.length) := by 525 | generalize hl: (List.zip a b) = s 526 | induction s generalizing a b 527 | case nil => 528 | simp_all only [unzip_nil] 529 | unfold List.zip at hl 530 | have hl:= List.zipWith_eq_nil_iff.mp hl 531 | cases hl; 532 | simp_all only [zipWith_nil_left, length_nil, ge_iff_le, Nat.zero_le, Nat.min_eq_left, take_zero] 533 | have: b.length = 0 :=by rename_i h; simp only [h, length_nil] 534 | simp_all only [zipWith_nil_right, length_nil, ge_iff_le, Nat.zero_le, Nat.min_eq_right, take_nil] 535 | case cons head tail ind => 536 | cases a; simp only [zip_nil_left, not_false_eq_true, not_sym] at hl 537 | cases b; simp only [zip_nil_right, not_false_eq_true, not_sym] at hl 538 | rename_i ha ta hb tb 539 | have tab: tail = List.zip ta tb := by simp_all only [zip_cons_cons, cons.injEq] 540 | simp_all only [zip_cons_cons, cons.injEq, and_true, unzip_cons, length_cons] 541 | have tka: List.take (min (succ ta.length) (succ tb.length)) (hb :: tb) 542 | = hb::List.take (min ta.length tb.length) tb :=by 543 | unfold List.take 544 | have : min (succ ta.length) (succ tb.length) = succ (min ta.length tb.length) 545 | :=by apply succ_min_succ 546 | simp [this] 547 | generalize ht : List.take (min ta.length tb.length) tb = t 548 | unfold List.take at ht 549 | simp only [ht] 550 | rw[tka] 551 | have : head.2 = hb :=by rw [hl.symm] 552 | simp only [this, cons.injEq, true_and] 553 | apply ind; simp only 554 | -------------------------------------------------------------------------------- /RustLeanModels/Iterator.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | import RustLeanModels.Basic 4 | import Lean 5 | open List 6 | open Mathlib 7 | open Nat 8 | open Char 9 | set_option maxHeartbeats 10000000 10 | 11 | 12 | /- 13 | next 14 | 15 | How to translate: 16 | Rust: let x = s.next() 17 | Lean: let x:= s.next.1 18 | let s:= s.next.2 19 | 20 | -/ 21 | 22 | namespace Iterator 23 | 24 | def next (s: List α) : (Option α) × List α := match s with 25 | | [] => (none , []) 26 | | h::t => (some h, t) 27 | 28 | /- 29 | peek 30 | -/ 31 | 32 | def peek (s: List α) : Option α := match s with 33 | | [] => none 34 | | _::[] => none 35 | | _::h1::_ => some h1 36 | 37 | /- 38 | flatten 39 | From: core::iter::adapers::flatten 40 | -/ 41 | 42 | def flatten (s: List (List α)):= List.foldl List.append [] s 43 | 44 | lemma fold_append_cons: List.foldl List.append (v::u) s = v:: List.foldl List.append u s :=by 45 | induction s generalizing v u; simp only [foldl_nil] 46 | rename_i h t ind; 47 | simp only [foldl_cons, append_eq, cons_append]; rw[ind] 48 | 49 | lemma fold_append_para1_elim : List.foldl List.append u l = u ++ (List.foldl List.append [] l) :=by 50 | induction l generalizing u 51 | simp only [foldl_nil, List.append_nil] 52 | rename_i h t ind ; 53 | simp only [foldl_cons, append_eq, List.nil_append] 54 | rw[@ind (u++h), @ind h]; simp only [List.append_assoc] 55 | 56 | 57 | lemma flatten_cons : flatten (h::t) = h ++ flatten t :=by 58 | unfold flatten ; simp only [foldl_cons, append_eq, List.nil_append] 59 | rw[fold_append_para1_elim] 60 | 61 | lemma flatten_append : flatten (l1 ++ l2) = flatten l1 ++ flatten l2 :=by 62 | induction l1 generalizing l2 63 | simp only [flatten, List.nil_append, foldl_nil] 64 | rename_i h t ind 65 | have: h :: t ++ l2 = h::(t++l2) := by simp only [cons_append] 66 | simp only [this, flatten_cons, ind, List.append_assoc] 67 | 68 | end Iterator 69 | -------------------------------------------------------------------------------- /RustLeanModels/Iterator.md: -------------------------------------------------------------------------------- 1 | 5 | ## Rust Iterator 6 | 7 | ## Data type conversion 8 | - The `Iterator` trait is converted to `List` in Lean. Many functions defined in Lean's core are 9 | equivalent to Rust functions that operate on iterators. 10 | 11 | 12 | ## Functions implemented in Iterator.lean 13 | 14 | | Rust Iterator function | Lean equivalent function | Description link | 15 | | ----------------------------- | ------------------- | ---------------------- | 16 | | std::iter::Iterator::flatten | flatten | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flatten | 17 | | std::iter::Iterator::next | next | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.next | 18 | | std::iter::Iterator::peek | peek | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.peek | 19 | 20 | ## Functions already defined in Lean 21 | 22 | | Rust Iterator function | Lean equivalent function | Description link | 23 | | ----------------------------- | ------------------- | ---------------------- | 24 | | std::iter::Iterator::all | List.all | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all | 25 | | std::iter::Iterator::any | List.any | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any | 26 | | std::iter::Iterator::chain | List.append | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain | 27 | | std::iter::Iterator::collect | self | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect | 28 | | std::iter::Iterator::count | List.length | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count | 29 | | std::iter::Iterator::emunerate | List.enum | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.enumerate | 30 | | std::iter::Iterator::fold | List.foldl | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold | 31 | | std::iter::Iterator::last | List.getLast | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last | 32 | | std::iter::Iterator::map | List.map | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map | 33 | | std::iter::Iterator::nth | List.get? | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth | 34 | | std::iter::Iterator::position | List.findSome? | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.position | 35 | | std::iter::Iterator::rev | List.reverse | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.rev | 36 | | std::iter::Iterator::unzip | List.unzip | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.unzip | 37 | | std::iter::Iterator::zip | List.zip | https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.zip | 38 | 39 | -------------------------------------------------------------------------------- /RustLeanModels/ProofExample.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | import RustLeanModels.Basic 4 | import RustLeanModels.RustString 5 | import RustLeanModels.UTF8Str 6 | import Lean 7 | open RustString 8 | open List 9 | open Mathlib 10 | open Nat 11 | set_option maxHeartbeats 10000000 12 | 13 | 14 | /-Sometimes adding an extra variable makes to proof easier-/ 15 | 16 | lemma substring_charIndex_map : substring_charIndex s ss = some i → 17 | ∃ j, substring_charIndex (List.map f (v ++ s)) (List.map f ss) = some j ∧ j ≤ i + v.length := by 18 | intro g 19 | have g:= substring_charIndex_some_sound.mp g 20 | unfold is_first_substringcharIndex at g 21 | have g:= g.left 22 | have gi : is_substringcharIndex (List.map f (v ++ s)) (List.map f ss) (i + v.length) := by 23 | unfold is_substringcharIndex at * ; obtain ⟨s0, s2, g⟩:= g 24 | use List.map f (v++s0), List.map f s2; 25 | simp only [g, List.append_assoc, map_append, length_append, length_map, true_and]; omega 26 | have gj := exists_first_substring_charIndex (by use (i + v.length)) 27 | obtain ⟨j, gj⟩ := gj; use j 28 | constructor 29 | . exact substring_charIndex_some_sound.mpr gj 30 | . unfold is_first_substringcharIndex at gj 31 | exact gj.right (i + v.length) gi 32 | 33 | lemma sub_split_map (gss: ss.length > 0): 34 | (split_substring s ss).length ≤ (split_substring (List.map f (v++s)) (List.map f ss)).length :=by 35 | generalize gl: s.length = l 36 | induction l using Nat.strong_induction_on generalizing s v 37 | rename_i n ind 38 | by_cases (s.length = 0) 39 | rename_i gs; have gs:= List.eq_nil_of_length_eq_zero gs 40 | rw[gs]; unfold split_substring; simp only [gt_iff_lt, gss, ↓reduceDIte, take_nil, drop_nil, 41 | map_nil, length_nil, lt_self_iff_false, singleton_append, length_cons, List.length_singleton, 42 | reduceSucc, ge_iff_le] 43 | have i:= @substring_charIndex_of_nil ss gss; rw[i] 44 | simp only [List.length_singleton, length_map, gss, ↓reduceDIte, ge_iff_le] 45 | split ; simp only [List.append_nil, List.length_singleton, le_refl]; 46 | simp only [List.append_nil,length_cons]; omega 47 | unfold split_substring; simp only [gt_iff_lt, gss, ↓reduceDIte, length_map, ge_iff_le] 48 | split; split; simp only [List.length_singleton, le_refl] 49 | simp only [List.length_singleton, length_cons]; simp only [length_nil, reduceSucc]; omega 50 | rename_i i gi 51 | have gj:= @substring_charIndex_map _ _ _ f v gi; obtain ⟨j,gj⟩ := gj; rw[gj.left] 52 | simp only [length_cons, ge_iff_le]; 53 | generalize gm: (List.drop (i + ss.length) s).length = m 54 | have id1: m < n :=by rw[← gm, ← gl]; simp only [length_drop]; omega 55 | rw[← List.map_drop] 56 | have gu: ∃ u, u ++ List.drop (i + ss.length) s = List.drop (j + ss.length) (v ++ s):= by 57 | by_cases j + ss.length ≤ v.length ; rename_i gx 58 | rw [List.drop_append_of_le_length gx] 59 | use (v.drop (j + ss.length)) ++ (s.take (i + ss.length)) 60 | simp only [List.append_assoc, take_append_drop] 61 | have : j + ss.length = v.length + (j + ss.length - v.length) := by omega 62 | rw[this, List.drop_append]; 63 | have : i + ss.length = ((i + ss.length) - (j + ss.length - v.length)) + (j + ss.length - v.length) := by omega 64 | rw[this, ← List.drop_drop] 65 | use List.take (i + ss.length - (j + ss.length - v.length)) (List.drop (j + ss.length - v.length) s) 66 | rw[List.take_append_drop] 67 | obtain⟨u, gu⟩:= gu; rw[← gu] 68 | exact Nat.succ_le_succ (@ind m id1 _ u gm) 69 | 70 | lemma split_map (gss: ss.length > 0): 71 | (split_substring s ss).length ≤ (split_substring (List.map f s) (List.map f ss)).length :=by 72 | have := @sub_split_map s ss f [] gss; simp only [nil_append] at this; exact this 73 | 74 | /- An example of using UTF8Str-/ 75 | lemma exists_Str_toUTF8_prefix_of_char_boundary: is_char_boundary s i → ∃ p, List.IsPrefix p s ∧ (Str_toUTF8 s).take i = Str_toUTF8 p :=by 76 | intro g 77 | generalize gx: PrefixFromPos s i = op 78 | have gx1:= gx 79 | simp only [PrefixFromPos, g, ↓reduceIte] at gx; symm at gx; rw[gx] at gx1 80 | generalize gp: PrefixFromPos_safe_r s i = p 81 | rw[gp] at gx1; have gx1:= PrefixFromPos_some_sound.mp gx1 82 | use p; simp only [gx1, true_and]; rw[← gx1.right]; exact Str_toUTF8_take_prefix gx1.left 83 | -------------------------------------------------------------------------------- /RustLeanModels/ProofTutorial.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | import RustLeanModels.Basic 4 | import RustLeanModels.RustString 5 | import Lean 6 | open RustString 7 | open List 8 | open Mathlib 9 | open Nat 10 | set_option maxHeartbeats 10000000 11 | 12 | 13 | /- Let's start with a simple example-/ 14 | /- This function is supposed to return the length of the longest commpn prefix of 2 strings s1 s2 -/ 15 | def LCP_length (s1 s2: Str) := match s1, s2 with 16 | | [] , _ => 0 17 | | _, [] => 0 18 | | h1::t1, h2::t2 => if h1 ≠ h2 then 0 else 1 + LCP_length t1 t2 19 | 20 | /- We want to verify the correctness of this function-/ 21 | /- Firstly, we need to formalize the correctness property as : -/ 22 | def is_LCP_length (s1 s2: Str) (l: Nat) := (∃ pre, List.IsPrefix pre s1 ∧ List.IsPrefix pre s2 ∧ pre.length = l) ∧ 23 | (∀ p, List.IsPrefix p s1 ∧ List.IsPrefix p s2 → p.length ≤ l) 24 | 25 | /- From the definition above, we formalize the correctness theorem of the LCP_length as: 26 | 27 | theorem LCP_length_correct : LCP_length s1 s2 = l ↔ is_LCP_length s1 s2 l 28 | -/ 29 | 30 | /- Firstly, we prove the modus ponen (mp) direction of the theorem by induction-/ 31 | lemma LCP_length_correct_mp : LCP_length s1 s2 = l → is_LCP_length s1 s2 l:= by 32 | induction s1 generalizing s2 l 33 | simp only [LCP_length]; intro g; rw[← g] 34 | simp only [is_LCP_length, prefix_nil, length_eq_zero, and_imp, forall_eq, _root_.nil_prefix, 35 | length_nil, _root_.zero_le, imp_self, and_true, exists_eq_left, and_self] 36 | rename_i h1 t1 ind 37 | cases s2 38 | . simp only [LCP_length]; intro g; rw[← g] 39 | simp only [is_LCP_length, prefix_nil, length_eq_zero, and_imp, and_self_left] 40 | constructor 41 | . use []; simp only [_root_.nil_prefix, and_self] 42 | . intro p _ g2; simp only [g2, length_nil, le_refl] 43 | . rename_i h2 t2; simp only [LCP_length, ne_eq, ite_not, is_LCP_length, and_imp] 44 | split 45 | . intro g; rename_i gh; 46 | have ind:= @ind t2 (l-1) (by omega); unfold is_LCP_length at ind; obtain ⟨pre, gp⟩:= ind.left 47 | constructor 48 | . use (h2::pre); rw[gh]; simp only [prefix_cons_inj, gp, length_cons, true_and]; omega 49 | . intro p0 g1p0 g2p0; cases p0; simp only [length_nil, _root_.zero_le] 50 | rename_i hp0 tp0; 51 | have g1p0:= prefix_cons g1p0; have g2p0:= prefix_cons g2p0; 52 | have ind:= ind.right tp0 ⟨g1p0.right, g2p0.right⟩ 53 | simp only [length_cons, ge_iff_le]; omega 54 | . intro g; rw[← g]; 55 | constructor 56 | . use [] 57 | simp only [_root_.nil_prefix, length_nil, nonpos_iff_eq_zero, length_eq_zero,true_and] 58 | . intro p gp1 gp2; cases p; simp only [length_nil, le_refl] 59 | rename_i gh hp tp; 60 | have gp1:= prefix_cons gp1; have gp2:= prefix_cons gp2; 61 | simp only [← gp1.left, ← gp2.left, not_true_eq_false] at gh 62 | 63 | /- For the modus ponen reverse (mpr) direction, we can prove it by induction, 64 | but it is more convenient if we use the "Eq_iff_of_unique_and_mp" theorem (Basic.lean) instead 65 | (see the definition of "unique" and "Eq_iff_of_unique_and_mp" in Basic.lean for more details) 66 | -/ 67 | 68 | /- First, we need to prove that the function "is_LCP_length s1 s2 : Nat → Prop" is "unique" 69 | (there is a only one value l such that "is_LCP_length s1 s2 l") 70 | -/ 71 | lemma is_LCP_length_unique: unique (is_LCP_length s1 s2) := by 72 | unfold unique; intro l1 l2 g1 g2 73 | unfold is_LCP_length at g1 g2 74 | have ⟨p1, g1l⟩ := g1.left; have ⟨p2, g2l⟩ := g2.left 75 | have g1r:= g1.right p2 ⟨g2l.left, g2l.right.left⟩ 76 | have g2r:= g2.right p1 ⟨g1l.left, g1l.right.left⟩ 77 | omega 78 | 79 | /- The correctness theorem-/ 80 | theorem LCP_length_correct : LCP_length s1 s2 = l ↔ is_LCP_length s1 s2 l := by 81 | apply Eq_iff_of_unique_and_mp is_LCP_length_unique (by intro l ; exact LCP_length_correct_mp) 82 | 83 | /-Sometimes the function is defined recursively with extra parameters. 84 | In this case, there is an _aux function with the extra parameters 85 | and a main functions which assign initial values for those parameters 86 | For example: -/ 87 | 88 | def LCP_length_aux (s1 s2: Str) (k: Nat) := match s1, s2 with 89 | | [] , _ => k 90 | | _, [] => k 91 | | h1::t1, h2::t2 => if h1 ≠ h2 then k else LCP_length_aux t1 t2 (k+1) 92 | 93 | def LCP_length_version2 (s1 s2: Str) := LCP_length_aux s1 s2 0 94 | 95 | /- 96 | This function is should be equivalent to LCP_length function. 97 | Supposed that we want to prove the equivalence of them. 98 | There are 2 ways to construct an induction proof for recursive functions with extra parameters (each with pros and cons): 99 | 1. Firstly, construct prove on the "_aux" function (as a general version). Then derive the proof for the main function. 100 | Pros: no extra lemmas. 101 | Cons: hard to write the theorem statement. 102 | 103 | 2. Directly on the main function. (Prefered) 104 | Pros: Write the theorem statement is straightforward. 105 | Cons: Requires lemmas for manipulating the extra parameters of the "_aux" theorem. 106 | However, those lemmas (name: "func_name_aux_para1_[some operation]") are very useful. 107 | -/ 108 | 109 | 110 | --Here is the demonstration of the first way 111 | 112 | lemma LCP_length_aux_EQ : LCP_length_aux s1 s2 k = k + LCP_length s1 s2 :=by 113 | induction s1 generalizing k s2 114 | simp only [LCP_length_aux, LCP_length, add_zero] 115 | rename_i h1 t1 ind 116 | cases s2 117 | . simp only [LCP_length_aux, LCP_length, add_zero] 118 | . simp only [LCP_length_aux, ne_eq, ite_not, LCP_length] 119 | split 120 | . rw[ind, add_assoc] 121 | . simp only [add_zero] 122 | 123 | theorem LCP_length_EQ_first_way : LCP_length_version2 s1 s2 = LCP_length s1 s2 :=by 124 | unfold LCP_length_version2; 125 | simp only [LCP_length_aux_EQ, zero_add] 126 | 127 | --Here is the demonstration of the second way 128 | 129 | lemma LCP_length_aux_para1_elim: LCP_length_aux s1 s2 k = k + LCP_length_aux s1 s2 0 :=by 130 | induction s1 generalizing k s2 131 | simp only [LCP_length_aux, add_zero] 132 | rename_i h1 t1 ind 133 | cases s2 134 | . simp only [LCP_length_aux, add_zero] 135 | . simp only [LCP_length_aux, ne_eq, ite_not, zero_add] 136 | split 137 | . rw[ind, @ind _ 1, add_assoc] 138 | . simp only [add_zero] 139 | 140 | theorem LCP_length_EQ_second_way : LCP_length_version2 s1 s2 = LCP_length s1 s2 :=by 141 | induction s1 generalizing s2 142 | simp only [LCP_length_version2, LCP_length_aux, LCP_length] 143 | rename_i h1 t1 ind 144 | cases s2 145 | . simp only [LCP_length_version2, LCP_length_aux, LCP_length] 146 | . simp only [LCP_length_version2, LCP_length_aux, ne_eq, zero_add, ite_not, LCP_length] 147 | split 148 | . -- use the parameter manipulating lemma here 149 | rw[LCP_length_aux_para1_elim, ← LCP_length_version2, ind] 150 | . simp only 151 | 152 | 153 | /- 154 | Let's move on to a more complicated function 155 | This example demonstrates a difficult case where both of the two ways above stuck, because 156 | 1. For the first way: It is very hard to state an "_aux" lemma (like "LCP_length_aux_EQ") 157 | 2. For the second way: We need a parameter manipulating lemma (like "LCP_length_aux_para1_elim") 158 | to prove the main theorem, but to prove the lemma, we need the main theorem 159 | To overcome the difficulty in the second way, we can use this trick: 160 | "When we need an induction assumption of A to prove B and vice versa, prove (A ∧ B) instead" " 161 | -/ 162 | 163 | /-The function "LCS_length" should calculate the length of the longest common substring of the two strings s1 s2-/ 164 | def LCS_length_aux (s1 s2: Str) (k: Nat) := match s1, s2 with 165 | | [] , _ => k 166 | | _, [] => k 167 | | h1::t1, h2::t2 => if h1 ≠ h2 then Nat.max (Nat.max (LCS_length_aux (h1::t1) t2 0) (LCS_length_aux t1 (h2::t2) 0)) k 168 | else Nat.max (Nat.max (LCS_length_aux (h1::t1) t2 0) (LCS_length_aux t1 (h2::t2) 0)) (LCS_length_aux t1 t2 (k+1)) 169 | termination_by s1.length + s2.length 170 | 171 | def LCS_length (s1 s2: Str) := LCS_length_aux s1 s2 0 172 | 173 | def is_LCS_length (s1 s2) (k:Nat):= (∀ s, (contains_substring s1 s ∧ contains_substring s2 s) → s.length ≤ k ) 174 | ∧ (∃ s, (contains_substring s1 s ∧ contains_substring s2 s ∧ s.length = k)) 175 | 176 | /- Here are some supporting lemmas, the main lemma "LCS_length_correct_mp" at line 355-/ 177 | 178 | 179 | /- Some supporting lemmas-/ 180 | 181 | lemma common_substring_cons: contains_substring (h1 :: t1) s = true ∧ contains_substring (h2 :: t2) s = true 182 | → (contains_substring t1 s) ∨ (contains_substring t2 s) ∨ (List.isPrefixOf s (h1 :: t1) ∧ List.isPrefixOf s (h2 :: t2)):=by 183 | intro g; cases s 184 | . simp only [contains_substring, isPrefixOf, and_self, or_self] 185 | . simp only [contains_substring, Bool.if_true_left, Bool.decide_eq_true, Bool.or_eq_true] at g 186 | cases g.left 187 | . cases g.right 188 | . rename_i g1 g2; simp only [g1, g2, and_self, or_true] 189 | . rename_i gc; simp only [gc, true_or, or_true] 190 | . rename_i gc; simp only [gc, true_or] 191 | 192 | lemma common_substring_cons_diff_head: h1 ≠ h2 → contains_substring (h1 :: t1) s = true ∧ contains_substring (h2 :: t2) s = true 193 | → (contains_substring t1 s) ∨ (contains_substring t2 s) :=by 194 | intro gh g; have g:= common_substring_cons g 195 | by_contra; rename_i gc; simp only [not_or, Bool.not_eq_true] at gc; 196 | simp only [gc, Bool.false_eq_true, not_false_eq_true, not_sym, false_or] at g 197 | cases s 198 | . simp only [contains_substring, Bool.true_eq_false, not_false_eq_true, not_sym, and_self] at gc 199 | . simp only [isPrefixOf, Bool.and_eq_true, beq_iff_eq] at g; 200 | simp only [← g.left.left, ← g.right.left, ne_eq, not_true_eq_false] at gh 201 | 202 | lemma is_LCS_length_nil_left : is_LCS_length [] s2 0 :=by 203 | unfold is_LCS_length ; 204 | simp only [nonpos_iff_eq_zero, length_eq_zero, and_imp, exists_eq_right_right, contains_substring, and_self, and_true] 205 | intro s; cases s; simp only [implies_true] 206 | simp only [contains_substring, Bool.false_eq_true, not_false_eq_true, not_sym, imp_false, Bool.not_eq_true, false_implies] 207 | 208 | lemma is_LCS_length_nil_right : is_LCS_length s1 [] 0 :=by 209 | unfold is_LCS_length ; 210 | simp only [nonpos_iff_eq_zero, length_eq_zero, and_imp, exists_eq_right_right, contains_substring, and_self, and_true] 211 | intro s; cases s; simp only [implies_true] 212 | simp only [contains_substring, Bool.false_eq_true, not_false_eq_true, not_sym, imp_self, implies_true] 213 | 214 | lemma is_LCS_le_of_contains : is_LCS_length s1 s2 l → (contains_substring s1 s ∧ contains_substring s2 s) → s.length ≤ l := by 215 | intro g; exact g.left s 216 | 217 | lemma is_LCS_length_unique : unique (is_LCS_length s1 s2) :=by 218 | unfold unique; intro l1 l2 g1 g2; 219 | obtain ⟨u1, gt1⟩:= g1.right; obtain ⟨u2, gt2⟩:= g2.right 220 | have g1:= g1.left u2 ⟨gt2.left, gt2.right.left⟩ 221 | have g2:= g2.left u1 ⟨gt1.left, gt1.right.left⟩ 222 | omega 223 | 224 | lemma LCP_length_le_LCS_length: is_LCS_length s1 s2 k → LCP_length s1 s2 ≤ k := by 225 | intro g 226 | generalize gl: LCP_length s1 s2 = l 227 | obtain ⟨pre, gl⟩ := (LCP_length_correct_mp gl).left 228 | simp only [← IsPrefix_isPrefixOf_EQ] at gl 229 | have g:=g.left pre ⟨contains_substring_of_isPrefixOf gl.left, contains_substring_of_isPrefixOf gl.right.left⟩ 230 | omega 231 | 232 | lemma LCP_length_ne_LCS_length: (¬∃ pre, pre <+: s1 ∧ pre <+: s2 ∧ is_LCS_length s1 s2 pre.length) 233 | → is_LCS_length s1 s2 ls → ¬ LCP_length s1 s2 = ls := by 234 | intro g g1 235 | by_contra; rename_i gc 236 | have gc:= LCP_length_correct.mp gc; obtain ⟨pre, gc⟩:= gc.left 237 | have: ∃ pre, pre <+: s1 ∧ pre <+: s2 ∧ is_LCS_length s1 s2 pre.length := by 238 | use pre; simp only [gc, true_and]; rename_i gl; exact g1 239 | contradiction 240 | 241 | lemma lcp_is_LCS_length: (∃ pre, pre <+: s1 ∧ pre <+: s2 ∧ is_LCS_length s1 s2 pre.length) 242 | → is_LCS_length s1 s2 ls → LCP_length s1 s2 = ls := by 243 | intro g g1; obtain ⟨pre, g⟩:=g 244 | generalize gl: LCP_length s1 s2 = l 245 | have gl1:= (LCP_length_correct_mp gl).right pre ⟨g.left, g.right.left⟩ 246 | have gl2:= LCP_length_le_LCS_length g.right.right 247 | have gl3:= is_LCS_length_unique pre.length ls g.right.right g1 248 | omega 249 | 250 | 251 | lemma is_LCS_length_add_head_left : (is_LCS_length t1 s2 lct) → (is_LCS_length (h1::t1) s2 lc) → (lc = lct) ∨ (lc = 1 + lct) :=by 252 | unfold is_LCS_length; intro g1 g2 253 | obtain ⟨s, gt2⟩:= g2.right; obtain ⟨s1, gt1⟩:= g1.right 254 | have e:= g2.left s1 ⟨contains_substring_cons gt1.left, gt1.right.left⟩ 255 | cases s 256 | . simp only [length_nil] at gt2 257 | apply Or.intro_left; omega 258 | . rename_i hs ts; simp only [length_cons] at gt2 259 | have i1:= g1.left ts ⟨contains_substring_cons_cons gt2.left, contains_substring_substring_cons gt2.right.left⟩ 260 | omega 261 | 262 | lemma is_LCS_length_add_head_right : (is_LCS_length s1 t2 lct) → (is_LCS_length s1 (h2::t2) lc) → (lc = lct) ∨ (lc = 1 + lct) :=by 263 | unfold is_LCS_length; intro g1 g2 264 | obtain ⟨s, gt2⟩:= g2.right; obtain ⟨s1, gt1⟩:= g1.right 265 | have e:= g2.left s1 ⟨gt1.left, contains_substring_cons gt1.right.left⟩ 266 | cases s 267 | . simp only [length_nil] at gt2 268 | apply Or.intro_left; omega 269 | . rename_i hs ts; simp only [length_cons] at gt2 270 | have i1:= g1.left ts ⟨contains_substring_substring_cons gt2.left, contains_substring_cons_cons gt2.right.left⟩ 271 | omega 272 | 273 | lemma is_LCS_length_add_head_left_right : (is_LCS_length t1 t2 lct) → (is_LCS_length (h1::t1) (h2::t2) lc) → (lc = lct) ∨ (lc = 1 + lct) :=by 274 | unfold is_LCS_length; intro g1 g2 275 | obtain ⟨s, gt2⟩:= g2.right; obtain ⟨s1, gt1⟩:= g1.right 276 | have e:= g2.left s1 ⟨contains_substring_cons gt1.left, contains_substring_cons gt1.right.left⟩ 277 | cases s 278 | . simp only [length_nil] at gt2 279 | apply Or.intro_left; omega 280 | . rename_i hs ts; simp only [length_cons] at gt2 281 | have i1:= g1.left ts ⟨contains_substring_cons_cons gt2.left, contains_substring_cons_cons gt2.right.left⟩ 282 | omega 283 | 284 | 285 | lemma LCP_length_aux_para1_add1_le: LCS_length_aux s1 s2 (k + 1) ≤ 1 + LCS_length_aux s1 s2 k := by 286 | generalize gl: s1.length + s2.length = l 287 | induction l using Nat.strong_induction_on generalizing s1 s2 k 288 | rename_i l ind 289 | unfold LCS_length_aux 290 | split; omega; omega; 291 | rename_i h1 t1 h2 t2 292 | split; exact max_add_right; 293 | generalize gm: t1.length + t2.length=m 294 | have id1: m < l := by rw[← gm, ← gl]; simp only [length_cons]; omega 295 | have ind:= @ind m id1 t1 t2 (k+1) gm 296 | have i1: ((LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0)).max (LCS_length_aux t1 t2 (k + 1 + 1)) ≤ 297 | ((LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0)).max (1 + LCS_length_aux t1 t2 (k + 1)) := max_le_of_right_le ind 298 | have i2:((LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0)).max (LCS_length_aux t1 t2 (k + 1) + 1) ≤ 299 | 1 + ((LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0)).max (LCS_length_aux t1 t2 (k + 1)) := max_add_right 300 | have e: LCS_length_aux t1 t2 (k + 1) + 1 = 1 + LCS_length_aux t1 t2 (k + 1) := by omega 301 | rw[e] at i2 ; omega 302 | 303 | 304 | lemma sup_LCS_correct1 : h1 = h2 → (¬∃ pre, pre <+: h1 :: t1 ∧ pre <+: h2 :: t2 ∧ is_LCS_length (h1 :: t1) (h2 :: t2) pre.length) 305 | → ¬∃ pre, pre <+: t1 ∧ pre <+: t2 ∧ is_LCS_length t1 t2 pre.length := by 306 | intro gh gc 307 | by_contra ; rename_i gp; obtain ⟨pre, gp⟩ :=gp 308 | have: ∃ pre, pre <+: h1 :: t1 ∧ pre <+: h2 :: t2 ∧ is_LCS_length (h1 :: t1) (h2 :: t2) pre.length:= by 309 | use (h2::pre); rw[gh]; simp only [prefix_cons_inj, gp, length_cons, true_and] 310 | unfold is_LCS_length ; have gp:=gp.right.right; unfold is_LCS_length at gp 311 | constructor; 312 | intro s gs; 313 | cases s; simp only [length_nil, le_add_iff_nonneg_left, _root_.zero_le] 314 | rename_i hs ts; simp only [length_cons, add_le_add_iff_right] 315 | simp only [contains_substring, isPrefixOf, Bool.and_eq_true, beq_iff_eq, Bool.if_true_left, 316 | Bool.decide_and, Bool.decide_eq_true, Bool.or_eq_true, decide_eq_true_eq] at gs 317 | cases gs 318 | rename_i gs1 gs2 319 | cases gs1 320 | cases gs2 321 | . rename_i gs1 gs2; exact gp.left ts ⟨contains_substring_of_isPrefixOf gs1.right, contains_substring_of_isPrefixOf gs2.right⟩ 322 | . rename_i gs1 gs2; exact gp.left ts ⟨contains_substring_of_isPrefixOf gs1.right, contains_substring_substring_cons gs2⟩ 323 | . cases gs2 324 | rename_i gs1 gs2; exact gp.left ts ⟨contains_substring_substring_cons gs1, contains_substring_of_isPrefixOf gs2.right⟩ 325 | rename_i gs1 gs2; exact gp.left ts ⟨contains_substring_substring_cons gs1, contains_substring_substring_cons gs2⟩ 326 | use (h2::pre); 327 | simp only [contains_substring, isPrefixOf_cons₂_self, Bool.if_true_left, 328 | Bool.decide_eq_true, Bool.or_eq_true, length_cons, and_true] 329 | rename_i gp0; rw[IsPrefix_isPrefixOf_EQ, IsPrefix_isPrefixOf_EQ]; simp only [gp0, true_or, and_self] 330 | contradiction 331 | 332 | 333 | lemma sup_LCS_correct2 (g1: ∃ pre, pre <+: h1 :: t1 ∧ pre <+: h2 :: t2 ∧ is_LCS_length (h1 :: t1) (h2 :: t2) pre.length) 334 | (g2: ¬∃ pre, pre <+: t1 ∧ pre <+: t2 ∧ is_LCS_length t1 t2 pre.length) 335 | (g3: is_LCS_length (h1 :: t1) (h2 :: t2) lc) (g4: is_LCS_length t1 t2 lct ) : (lc = lct) ∧ (1 + LCP_length t1 t2 = lct):= by 336 | have g0:= lcp_is_LCS_length g1 g3; 337 | obtain⟨pre, g1⟩:=g1 338 | have g1r:= is_LCS_length_unique pre.length lc g1.right.right g3 339 | simp only [not_exists, not_and] at g2 340 | have e:= is_LCS_length_add_head_left_right g4 g3 341 | cases pre 342 | . simp only [_root_.nil_prefix, length_nil, true_and] at g1r 343 | have g1: 0 = lct :=by omega 344 | have g2:= g2 []; 345 | simp only [_root_.nil_prefix, length_nil, g1, g4, not_true_eq_false, imp_false] at g2 346 | . rename_i hp tp 347 | have gt1:= prefix_cons g1.left; have gt2:= prefix_cons g1.right.left; 348 | simp only [LCP_length, ← gt1.left, ← gt2.left, ne_eq, not_true_eq_false, ↓reduceIte] at g0 349 | simp only [g0, and_self] 350 | cases e; assumption; rename_i e 351 | simp only [length_cons] at g1r 352 | have e1: lct = tp.length := by omega 353 | have g2:= g2 tp gt1.right gt2.right 354 | rw[e1] at g4; contradiction 355 | 356 | /- 357 | Only the first Prop of the right-hand-side is the one we want to prove. 358 | However, to prove that we need to prove the other Props, which servers 359 | as the parameter manipulating lemmas. 360 | The design of the right-hand-side requires some back and forth. 361 | -/ 362 | lemma LCS_length_correct_mp: (LCS_length s1 s2 = lc) → (is_LCS_length s1 s2 lc ∧ 363 | ((∃ pre,List.IsPrefix pre s1 ∧ List.IsPrefix pre s2 ∧ is_LCS_length s1 s2 pre.length ) → LCS_length_aux s1 s2 k = k + lc) ∧ 364 | ((¬ ∃ pre, List.IsPrefix pre s1 ∧ List.IsPrefix pre s2 ∧ is_LCS_length s1 s2 pre.length) 365 | → ((k + LCP_length s1 s2 ≤ lc → LCS_length_aux s1 s2 k = LCS_length_aux s1 s2 0) ∧ 366 | (k + LCP_length s1 s2 > lc → LCS_length_aux s1 s2 k = k + LCP_length s1 s2 ) ) )):=by 367 | --cleaning trivial cases 368 | generalize gl: s1.length + s2.length = l 369 | induction l using Nat.strong_induction_on generalizing s1 s2 k lc 370 | rename_i l ind 371 | intro g; have gsv:= g; unfold LCS_length LCS_length_aux at g 372 | split at g 373 | . rw[← g]; simp only [is_LCS_length_nil_left, prefix_nil, exists_eq_left, _root_.nil_prefix, 374 | length_nil, and_self, LCS_length_aux, add_zero, imp_self, not_true_eq_false, nonpos_iff_eq_zero, 375 | add_eq_zero, and_imp, gt_iff_lt, add_pos_iff, self_eq_add_right, false_implies] 376 | . rw[← g]; simp only [is_LCS_length_nil_right, prefix_nil, LCS_length_aux, add_zero, implies_true, not_exists, 377 | not_and, LCP_length, nonpos_iff_eq_zero, imp_self, gt_iff_lt, and_self] 378 | --main case: s1, s2 ≠ nil 379 | -- get all inductions and inequalities 380 | rename_i h1 t1 h2 t2 381 | generalize gm: (h1::t1).length + t2.length = m 382 | have id1: m < l := by rw[← gm, ← gl]; simp only [length_cons]; omega 383 | generalize gh1: LCS_length_aux (h1::t1) t2 0 = lch1 384 | have indh1:= (@ind m id1 (h1::t1) t2 lch1 1 gm gh1).left 385 | generalize gm: t1.length + (h2::t2).length = m 386 | have id1: m < l := by rw[← gm, ← gl]; simp only [length_cons]; omega 387 | generalize gh2: LCS_length_aux t1 (h2::t2) 0 = lch2 388 | have indh2:= (@ind m id1 t1 (h2::t2) lch2 1 gm gh2).left 389 | generalize gm: t1.length + t2.length = m 390 | have id1: m < l := by rw[← gm, ← gl]; simp only [length_cons]; omega 391 | generalize gt: LCS_length_aux t1 t2 0 = lct 392 | have indtp1:= @ind m id1 t1 t2 lct (k+1) gm gt 393 | have indtp:= @ind m id1 t1 t2 lct k gm gt 394 | have indt1:= @ind m id1 t1 t2 lct 1 gm gt 395 | have ilch1:= is_LCS_length_add_head_left indt1.left indh1 396 | have ilch2:= is_LCS_length_add_head_right indt1.left indh2 397 | have i00: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≤ 1 + lct:= by rw [Nat.max_le]; omega 398 | have i01: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≥ LCS_length_aux t1 (h2 :: t2) 0 := by 399 | simp only [ge_iff_le,le_max_iff, le_refl, or_true] 400 | have i01: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≥ LCS_length_aux (h1 :: t1) t2 0 := by 401 | simp only [ge_iff_le, le_max_iff, le_refl, true_or] 402 | have i1: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≥ lct:= by omega 403 | have i2: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≤ lc:= by 404 | split at g; 405 | . rw[← g]; simp only [le_max_iff, _root_.zero_le, or_self, max_eq_left, le_refl] 406 | . rw[← g]; simp only [zero_add, le_max_iff, le_refl, max_le_iff, true_or] 407 | have e0: (lc = lct) ∨ (lc = lct + 1):= by 408 | split at g 409 | . simp only [le_max_iff, _root_.zero_le, or_self, max_eq_left] at g; omega 410 | . have := @LCP_length_aux_para1_add1_le t1 t2 0 411 | have u: lc ≤ 1 + lct :=by rw[←g, Nat.max_le]; omega 412 | omega 413 | have ih1: lch1 ≤ lc := by omega 414 | have ih2: lch2 ≤ lc := by omega 415 | --prove is_LCS_length (h1 :: t1) (h2 :: t2) lc 416 | have glc: is_LCS_length (h1 :: t1) (h2 :: t2) lc := by 417 | split at g 418 | . unfold is_LCS_length; constructor 419 | . intro s gs; rename_i gh 420 | have gc:= common_substring_cons_diff_head gh gs 421 | cases gc 422 | . rename_i gc; have:= is_LCS_le_of_contains indh2 ⟨gc, gs.right⟩ ; omega 423 | . rename_i gc; have:= is_LCS_le_of_contains indh1 ⟨gs.left, gc⟩; omega 424 | . simp only [le_max_iff, _root_.zero_le, or_self, max_eq_left] at g; symm at g; 425 | have g:= cases_max g; cases g 426 | . obtain⟨s, indh1⟩ := indh1.right 427 | rename_i g; rw[g, gh1]; use s 428 | simp only [indh1, contains_substring_cons, and_self] 429 | . obtain⟨s, indh2⟩ := indh2.right 430 | rename_i g; rw[g, gh2]; use s 431 | simp only [indh2, contains_substring_cons, and_self] 432 | . unfold is_LCS_length; constructor 433 | . intro s gs; rename_i gh; simp only [ne_eq, Decidable.not_not] at gh; 434 | cases s; simp only [length_nil, _root_.zero_le]; rename_i hs ts 435 | have gs:= common_substring_cons gs 436 | cases gs 437 | . rename_i gc; have:= is_LCS_le_of_contains indh2 ⟨gc, gs.right⟩ ; omega 438 | . rename_i gs; cases gs 439 | . rename_i gc; have:= is_LCS_le_of_contains indh1 ⟨gs.left, gc⟩ ; omega 440 | . rename_i gs; simp only [isPrefixOf, Bool.and_eq_true, beq_iff_eq] at gs 441 | have i0:= is_LCS_le_of_contains indtp.left ⟨contains_substring_of_isPrefixOf gs.left.right, contains_substring_of_isPrefixOf gs.right.right⟩ 442 | simp only [length_cons, ge_iff_le] 443 | by_contra 444 | have e: ts.length = lct :=by omega 445 | have : ∃ pre, pre <+: t1 ∧ pre <+: t2 ∧ is_LCS_length t1 t2 pre.length:= by 446 | use ts; rw[← IsPrefix_isPrefixOf_EQ, ← IsPrefix_isPrefixOf_EQ, e]; 447 | simp only [gs, indtp, and_self] 448 | have indt1:= indt1.right.left this 449 | have : lc ≥ LCS_length_aux t1 t2 1 :=by 450 | simp only [← g, zero_add, ge_iff_le, le_max_iff, le_refl, or_true] 451 | omega 452 | . simp only [zero_add] at g; symm at g; 453 | have g:= cases_max g 454 | cases g 455 | . rename_i g; have g:= cases_max g 456 | cases g 457 | . obtain⟨s, indh1⟩ := indh1.right 458 | rename_i g; rw[g, gh1]; use s 459 | simp only [indh1, contains_substring_cons, and_self] 460 | . obtain⟨s, indh2⟩ := indh2.right 461 | rename_i g; rw[g, gh2]; use s 462 | simp only [indh2, contains_substring_cons, and_self] 463 | . by_cases (¬ ∃ pre, List.IsPrefix pre t1 ∧ List.IsPrefix pre t2 ∧ is_LCS_length t1 t2 pre.length) 464 | . rename_i gc 465 | have i1: LCP_length t1 t2 ≤ lct := LCP_length_le_LCS_length indt1.left 466 | have i2 := LCP_length_ne_LCS_length gc indt1.left 467 | have : 1 + LCP_length t1 t2 ≤ lct := by omega 468 | have ind3:= indt1.right.right gc 469 | simp only [this, true_implies, gt_iff_lt, isEmpty_Prop, not_lt, IsEmpty.forall_iff, and_true] at ind3 470 | rename_i g0 ; rw[g0, ind3, gt] 471 | have ind1:= indt1.left; unfold is_LCS_length at ind1; obtain ⟨s, ind1⟩ := ind1.right 472 | use s; simp only [ind1, contains_substring_cons, and_self] 473 | . have ind2:= indt1.right.left; 474 | rename_i gh g0 gc; rw[not_not] at gc; have ind2:= ind2 gc 475 | obtain ⟨pre, gc⟩ := gc; simp only [ne_eq, Decidable.not_not] at gh; rw[gh] 476 | use h2::pre; simp only [contains_substring, isPrefixOf_cons₂_self, IsPrefix_isPrefixOf_EQ, gc, 477 | ↓reduceIte, length_cons, true_and]; 478 | rw[g0, ind2]; 479 | have: pre.length = lct := is_LCS_length_unique pre.length lct gc.right.right indt1.left 480 | omega 481 | simp only [glc, true_and] 482 | --prove support Prop 1 483 | constructor 484 | . intro gp; 485 | simp only [LCS_length_aux, ne_eq, ite_not] 486 | split 487 | . rename_i gh; simp only [gh, ne_eq, not_true_eq_false, ↓reduceIte, zero_add] at g 488 | by_cases (∃ pre, pre <+: t1 ∧ pre <+: t2 ∧ is_LCS_length t1 t2 pre.length) 489 | . rename_i gc 490 | have gp1:= lcp_is_LCS_length gp glc; have gp2:= lcp_is_LCS_length gc indt1.left; 491 | simp only [LCP_length, gh, ne_eq, not_true_eq_false, ↓reduceIte] at gp1 492 | have e2:= indtp1.right.left gc 493 | have: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≤ LCS_length_aux t1 t2 (k + 1) := by omega 494 | simp only [this, max_eq_right]; omega 495 | . rename_i gc 496 | have e1 := sup_LCS_correct2 gp gc glc indt1.left 497 | have e2:= (indt1.right.right gc).left (by omega) 498 | by_cases (k + 1 + LCP_length t1 t2 ≤ lct) 499 | . rename_i gp1 500 | have e1:= (indtp1.right.right gc).left gp1 501 | have e3: k = 0 := by omega 502 | rw[e1, ← e2, gh, g, e3]; simp only [zero_add] 503 | . rename_i gp1; simp only [not_le] at gp1 504 | have e1:= (indtp1.right.right gc).right gp1 505 | have : (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≤ LCS_length_aux t1 t2 (k + 1) := by omega 506 | simp only [this, max_eq_right]; omega 507 | . rename_i gh; 508 | simp only [ne_eq, gh, not_false_eq_true, not_sym, ↓reduceIte, le_max_iff, _root_.zero_le, or_self, max_eq_left] at g 509 | obtain⟨pre, gp⟩:=gp 510 | cases pre 511 | . simp only [_root_.nil_prefix, length_nil, true_and] at gp 512 | have e:= is_LCS_length_unique lc 0 glc gp 513 | simp only [g, e, _root_.zero_le, max_eq_right, add_zero]; 514 | . simp only [← IsPrefix_isPrefixOf_EQ, isPrefixOf, Bool.and_eq_true, beq_iff_eq, 515 | length_cons] at gp 516 | rw[← gp.left.left, ← gp.right.left.left] at gh; contradiction 517 | --prove support Prop 2 518 | . intro gp 519 | simp only [LCP_length, ne_eq, ite_not, LCS_length_aux, le_max_iff, _root_.zero_le, or_self, max_eq_left, 520 | zero_add, gt_iff_lt] 521 | split 522 | . rename_i gh; simp only [gh, ne_eq, not_true_eq_false, ↓reduceIte, zero_add] at g 523 | have := sup_LCS_correct1 gh gp 524 | have indtp1:= indtp1.right.right this 525 | have indt1:= (indt1.right.right this).left 526 | constructor 527 | . intro glcp 528 | cases e0 529 | . have e1:= indtp1.left (by omega) 530 | have e2:= indt1 (by omega) 531 | rw[e1,← e2] 532 | . have e2:= (indtp.right.right this).left (by omega) 533 | have i1:= @LCP_length_aux_para1_add1_le t1 t2 k 534 | have i2 := LCP_length_ne_LCS_length this indtp.left 535 | have indt1:= indt1 (by omega) 536 | have i3: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≥ LCS_length_aux t1 t2 1 :=by omega 537 | rw[gh] at i3; simp only [i3, max_eq_left] at g 538 | have i4: (LCS_length_aux (h2 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≥ LCS_length_aux t1 t2 (k+1) :=by omega 539 | rw[gh]; simp only [i3, i4, max_eq_left] 540 | . intro glcp 541 | have indtp1:= indtp1.right (by omega) 542 | have: (LCS_length_aux (h1 :: t1) t2 0).max (LCS_length_aux t1 (h2 :: t2) 0) ≤ LCS_length_aux t1 t2 (k + 1):= by omega 543 | simp only [this, max_eq_right]; omega 544 | . rename_i gh; simp only [ne_eq, gh, not_false_eq_true, not_sym, ↓reduceIte, le_max_iff, 545 | _root_.zero_le, or_self, max_eq_left] at g 546 | constructor 547 | . intro g0; simp only [add_zero, ← g, le_max_iff] at g0 548 | simp only [le_max_iff, g0, max_eq_left] 549 | . intro g0; simp only [add_zero] at g0; have g0:=le_of_lt g0 550 | rw[g]; simp only [g0, max_eq_right, add_zero] 551 | 552 | theorem LCS_length_correct: LCS_length s1 s2 = l ↔ is_LCS_length s1 s2 l :=by 553 | apply Eq_iff_of_unique_and_mp is_LCS_length_unique 554 | intro l g; exact (@LCS_length_correct_mp s1 s2 l 1 g).left 555 | -------------------------------------------------------------------------------- /RustLeanModels/RustString.md: -------------------------------------------------------------------------------- 1 | 5 | ## Rust String 6 | 7 | ## Data type conversion 8 | - String is converted to `List Char` in Lean. This helps constructing induction proofs. 9 | - The integer types that are used in String indexing are converted to `Nat` in the Lean implementation. 10 | This assumes that overflow exceptions will not happen. Note that overflow exceptions can only happen 11 | in Rust programs which use usize for String indexing when the Strings size are GBs. 12 | - Byte Lists (for UTF8 conversion) are represented as `List Nat` in Lean. Strings are converted from `List Char` to `List Nat` by the function `Str_toUTF8`. 13 | This function ensures that the output is a valid UTF8 string. We use three axioms: `Char_Pos0`, `Char_Diff`, and `Char_Size` which describe 14 | the properties of UTF8 encoding (see [RustLeanModels/UTF8Str.lean](UTF8Str.lean)). 15 | - The trait std::str::pattern::Pattern is converted to the inductive type `Pattern` (see RustString.lean). 16 | 17 | 18 | 19 | ## Functions defined in RustString 20 | | Rust String function | Equivalent Lean function | Description link | 21 | | ----------------------------- | ------------------- | ---------------------- | 22 | | core::str::bytes | UTF8Str.Str_toUTF8 | https://doc.rust-lang.org/std/primitive.str.html#method.bytes | 23 | | core::str::ceil_char_boundary | ceil_char_boundary | https://doc.rust-lang.org/std/primitive.str.html#method.ceil_char_boundary | 24 | | core::str::char_indices | char_indices | https://doc.rust-lang.org/std/primitive.str.html#method.char_indices | 25 | | core::str::contains | contains | https://doc.rust-lang.org/std/primitive.str.html#method.contains | 26 | | core::str::ends_with | ends_with | https://doc.rust-lang.org/std/primitive.str.html#method.ends_with | 27 | | core::str::eq_ignore_ascii_case | eq_ignore_ascii_case | https://doc.rust-lang.org/std/primitive.str.html#method.eq_ignore_ascii_case | 28 | | core::str::find | find | https://doc.rust-lang.org/std/primitive.str.html#method.find | 29 | | core::str::get | string_slice | https://doc.rust-lang.org/std/primitive.str.html#method.get | 30 | | core::str::is_ascii | is_ascii | https://doc.rust-lang.org/std/primitive.str.html#method.is_ascii | 31 | | core::str::is_char_boundary | is_char_boundary | https://doc.rust-lang.org/std/primitive.str.html#method.is_char_boundary | 32 | | core::str::lines | lines | https://doc.rust-lang.org/std/primitive.str.html#method.lines | 33 | | core::str::match_indices | match_indices | https://doc.rust-lang.org/std/primitive.str.html#method.match_indices | 34 | | core::str::matches | rust_matches | https://doc.rust-lang.org/std/primitive.str.html#method.matches | 35 | | core::str::repeat | rust_repeat | https://doc.rust-lang.org/std/primitive.str.html#method.repeat | 36 | | core::str::replace | replace | https://doc.rust-lang.org/std/primitive.str.html#method.replace | 37 | | core::str::replacen | replacen | https://doc.rust-lang.org/std/primitive.str.html#method.replacen | 38 | | core::str::rfind | rfind | https://doc.rust-lang.org/std/primitive.str.html#method.rfind | 39 | | core::str::rmatch_indices | rmatch_indices | https://doc.rust-lang.org/std/primitive.str.html#method.rmatch_indices | 40 | | core::str::rsplit | rsplit | https://doc.rust-lang.org/std/primitive.str.html#method.rsplit | 41 | | core::str::rsplit_once | rsplit_once | https://doc.rust-lang.org/std/primitive.str.html#method.rsplit_once | 42 | | core::str::rsplit_terminator | rsplit_terminator | https://doc.rust-lang.org/std/primitive.str.html#method.rsplit_terminator | 43 | | core::str::split | split | https://doc.rust-lang.org/std/primitive.str.html#method.split | 44 | | core::str::split_ascii_whitespace | split_ascii_whitespace | https://doc.rust-lang.org/std/primitive.str.html#method.split_ascii_whitespace | 45 | | core::str::split_at | split_at_checked | https://doc.rust-lang.org/std/primitive.str.html#method.split_at | 46 | | core::str::split_inclusive | split_inclusive | https://doc.rust-lang.org/std/primitive.str.html#method.split_inclusive | 47 | | core::str::split_once | split_once | https://doc.rust-lang.org/std/primitive.str.html#method.split_once | 48 | | core::str::split_terminator | split_terminator | https://doc.rust-lang.org/std/primitive.str.html#method.split_terminator | 49 | | core::str::split_whitespace | split_whitespace | https://doc.rust-lang.org/std/primitive.str.html#method.split_whitespace | 50 | | core::str::splitn | splitn | https://doc.rust-lang.org/std/primitive.str.html#method.splitn | 51 | | core::str::starts_with | starts_with | https://doc.rust-lang.org/std/primitive.str.html#method.starts_with | 52 | | core::str::strip_prefix | strip_prefix | https://doc.rust-lang.org/std/primitive.str.html#method.strip_prefix | 53 | | core::str::strip_suffix | strip_suffix | https://doc.rust-lang.org/std/primitive.str.html#method.strip_suffix | 54 | | core::str::to_ascii_lowercase | to_ascii_lowercase | https://doc.rust-lang.org/std/primitive.str.html#method.to_ascii_lowercase | 55 | | core::str::to_ascii_uppercase | to_ascii_uppercase | https://doc.rust-lang.org/std/primitive.str.html#method.to_ascii_uppercase | 56 | | core::str::to_lowercase | N/A | https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase | 57 | | core::str::to_uppercase | N/A | https://doc.rust-lang.org/std/primitive.str.html#method.to_uppercase | 58 | | core::str::trim | trim | https://doc.rust-lang.org/std/primitive.str.html#method.trim | 59 | | core::str::trim_ascii | trim_ascii | https://doc.rust-lang.org/std/primitive.str.html#method.trim_ascii | 60 | | core::str::trim_ascii_end | trim_ascii_end | https://doc.rust-lang.org/std/primitive.str.html#method.trim_ascii_end | 61 | | core::str::trim_ascii_start | trim_ascii_start | https://doc.rust-lang.org/std/primitive.str.html#method.trim_ascii_start | 62 | | core::str::trim_end | trim_end | https://doc.rust-lang.org/std/primitive.str.html#method.trim_end | 63 | | core::str::trim_end_matches | trim_end_matches | https://doc.rust-lang.org/std/primitive.str.html#method.trim_end_matches | 64 | | core::str::trim_matches | trim_matches | https://doc.rust-lang.org/std/primitive.str.html#method.trim_matches | 65 | | core::str::trim_start | trim_start | https://doc.rust-lang.org/std/primitive.str.html#method.trim_start | 66 | | core::str::trim_start_matches | trim_start_matches | https://doc.rust-lang.org/std/primitive.str.html#method.trim_start_matches | 67 | 68 | 69 | 70 | ## Functions that already exist in Lean 71 | | Rust String function | Lean equivalent function | Description link | 72 | | ----------------------------- | ------------------- | ---------------------- | 73 | | core::str::chars | self | https://doc.rust-lang.org/std/primitive.str.html#method.chars | 74 | | core::str::is_empty | List.isEmpty | https://doc.rust-lang.org/std/primitive.str.html#method.is_empty | 75 | | core::str::len | List.length | https://doc.rust-lang.org/std/primitive.str.html#method.len | 76 | 77 | 78 | ## Limitations 79 | 80 | The RustString library does not include: 81 | - Functions that mutate their input (e.g., `make_ascii_lowercase`), but it includes the cloning version (e.g. `to_ascii_lowercase`). 82 | - Functions which may panic (e.g., slice indexing), but it includes the non-panicking version (e.g. `str::get`) -------------------------------------------------------------------------------- /RustLeanModels/UTF8Str.lean: -------------------------------------------------------------------------------- 1 | -- Copyright Kani Contributors 2 | -- SPDX-License-Identifier: Apache-2.0 OR MIT 3 | import RustLeanModels.Basic 4 | import RustLeanModels.RustString 5 | import Lean 6 | open Char 7 | open List 8 | open Mathlib 9 | open RustString 10 | open Nat 11 | set_option maxHeartbeats 10000000 12 | 13 | 14 | --TODO: prove the axiom from utf8EncodeChar function in Data/String/Extra.lean 15 | opaque toByte (c: Char) (i: Nat) (gi: i < c.utf8Size) : Nat 16 | 17 | /- 18 | The following three axioms are stated according to the description of UTF8 encoding 19 | Source: https://en.wikipedia.org/wiki/UTF-8#:~:text=UTF%2D8%20is%20a%20variable,Unicode%20Standard 20 | 21 | Axiom "Char_firstbyte_ne_others" is based on the fact that only the first bytes of all characters 22 | start with '0' or '11', while all other bytes start with '10'. In other words, the values of the 23 | first bytes of all characters are outside of the range [128, 191], while the value of other bytes 24 | are inside of the range. 25 | 26 | Axiom "Char_size_eq_of_firstbyte_eq" is based on the fact that the number of bytes used to encode 27 | a character is determined by its first byte. Specifically, if the first byte starts with '0', 28 | '11', '111', '1111', then the number of bytes is 1, 2, 3, 4 accordingly. 29 | 30 | Axiom "exists_byte_ne_of_Chat_ne" is based on the fact that if two characters are not equal, then 31 | at some (byte) position in their encoding, the two corresponding bytes are different. 32 | -/ 33 | 34 | axiom Char_firstbyte_ne_others {c1 c2: Char} {i: Nat} {gi: i < c2.utf8Size}: 35 | i ≠ 0 → toByte c1 0 (by linarith [@utf8Size_pos c1]) ≠ toByte c2 i gi 36 | 37 | axiom Char_size_eq_of_firstbyte_eq {c1 c2: Char} : toByte c1 0 (by linarith [@utf8Size_pos c1]) = toByte c2 0 (by linarith [@utf8Size_pos c2]) 38 | → utf8Size c1 = utf8Size c2 39 | 40 | axiom exists_byte_ne_of_Chat_ne {c1 c2: Char} : 41 | c1 ≠ c2 → ∃ i g1 g2, toByte c1 i g1 ≠ toByte c2 i g2 42 | 43 | /- 44 | The Char_toUTF8 function converts a Char into it UTF8 encoding. 45 | Char_toUTF8 is the same as String.utf8EncodeChar, but it is defined based on the opaque function toByte, 46 | so we can perform reasoning on it through the axioms. 47 | -/ 48 | def Char_toUTF8_aux (c : Char) (i : Nat) (_: i ≤ c.utf8Size): List Nat := match i with 49 | | 0 => [] 50 | | succ n => have gh: (utf8Size c - succ n) < utf8Size c := by omega 51 | have gt: n ≤ utf8Size c :=by omega 52 | toByte c (utf8Size c - succ n) gh :: (Char_toUTF8_aux c n gt) 53 | 54 | def Char_toUTF8 (c : Char) := Char_toUTF8_aux c (utf8Size c) (by omega) 55 | 56 | /- Convert a string s into it UTF8 encoding-/ 57 | def Str_toUTF8 (s: Str) : List Nat := match s with 58 | | [] => [] 59 | | h::t => Char_toUTF8 h ++ Str_toUTF8 t 60 | 61 | lemma Char_toUTF8_aux_ne_nil (g: i > 0) : Char_toUTF8_aux c i gi ≠ [] := by 62 | induction i ;contradiction 63 | rename_i n ind 64 | unfold Char_toUTF8_aux 65 | cases n ; simp only [zero_eq, reduceSucc, ne_eq, not_false_eq_true, not_sym] 66 | simp only [gt_iff_lt, zero_lt_succ, ne_eq, forall_true_left] at ind 67 | simp only [ne_eq, not_false_eq_true, not_sym] 68 | 69 | lemma Char_toUTF8_ne_nil : Char_toUTF8 c ≠ [] := by 70 | apply Char_toUTF8_aux_ne_nil; apply utf8Size_pos 71 | 72 | lemma Char_toUTF8_aux_length: (Char_toUTF8_aux c i gi).length = i :=by 73 | induction i; simp only [Char_toUTF8_aux, length_nil] 74 | rename_i n ind 75 | unfold Char_toUTF8_aux; simp only [succ_eq_add_one, length_cons, add_left_inj] 76 | cases n; simp only [Char_toUTF8_aux, length_nil] 77 | unfold Char_toUTF8_aux; simp only [succ_eq_add_one, length_cons, add_left_inj] 78 | simp only [Char_toUTF8_aux, succ_eq_add_one, length_cons, add_left_inj] at ind 79 | rename_i n ; have g: n + 1 ≤ c.utf8Size := by omega 80 | exact (@ind g) 81 | 82 | lemma Char_toUTF8_length : (Char_toUTF8 c).length = utf8Size c := Char_toUTF8_aux_length 83 | 84 | lemma utf8Size_sub_lt {c: Char} {i: Nat}: c.utf8Size - i.succ < c.utf8Size := by 85 | have:= @utf8Size_pos c; omega 86 | 87 | lemma all_bytes_eq_of_Char_eq_aux: Char_toUTF8_aux c1 n g1= Char_toUTF8_aux c2 n g2 88 | → ∀ i < n, toByte c1 (utf8Size c1 - succ i) utf8Size_sub_lt = toByte c2 (utf8Size c2 - succ i) utf8Size_sub_lt :=by 89 | induction n generalizing c1 c2 90 | unfold Char_toUTF8_aux 91 | simp only [zero_eq, not_lt_zero', IsEmpty.forall_iff, forall_const, forall_true_left] 92 | rename_i n ind 93 | intro g x gx 94 | unfold Char_toUTF8_aux at g 95 | have g:= List.cons_eq_cons.mp g 96 | have g1 : n ≤ utf8Size c1 := by omega 97 | have g2 : n ≤ utf8Size c2 := by omega 98 | have ind:= @ind c1 g1 c2 g2 g.right; 99 | by_cases (x < n); rename_i g0 100 | simp only [g0, ind] 101 | have g0: x = n := by linarith 102 | rw[g0]; exact g.left 103 | 104 | lemma eq_utf8Size_of_eq_Char_toUTF8: Char_toUTF8 c1 = Char_toUTF8 c2 → c1.utf8Size = c2.utf8Size:= by 105 | unfold Char_toUTF8 Char_toUTF8_aux 106 | have c1p:= @utf8Size_pos c1 107 | have c2p:= @utf8Size_pos c2 108 | split; omega; split; omega 109 | rename_i g1 _ _ _ _ _ g2 _ 110 | intro g 111 | simp only [g1, succ_eq_add_one, le_refl, tsub_eq_zero_of_le, g2, cons.injEq] at g 112 | exact Char_size_eq_of_firstbyte_eq g.left 113 | 114 | lemma all_bytes_eq_of_Char_eq (g: Char_toUTF8 c1 = Char_toUTF8 c2): 115 | toByte c1 i g1 = toByte c2 i (by linarith [eq_utf8Size_of_eq_Char_toUTF8 g]) := by 116 | have gs:= eq_utf8Size_of_eq_Char_toUTF8 g 117 | unfold Char_toUTF8 at g 118 | simp only [gs] at g 119 | have g:= all_bytes_eq_of_Char_eq_aux g (utf8Size c1 - succ i) (by omega) 120 | have e1: c1.utf8Size - (c1.utf8Size - i.succ).succ = i :=by omega 121 | have e2: c2.utf8Size - (c1.utf8Size - i.succ).succ = i :=by omega 122 | simp only [succ_eq_add_one, e1, e2] at g 123 | exact g 124 | 125 | lemma Char_toUTF8_eq_iff_eq: Char_toUTF8 c1 = Char_toUTF8 c2 ↔ c1 = c2 :=by 126 | constructor 127 | intro g; by_contra; rename_i gc 128 | have gc:= exists_byte_ne_of_Chat_ne gc; obtain ⟨i, g1, _, gc⟩:= gc 129 | have gx:= @all_bytes_eq_of_Char_eq c1 c2 i g1 g 130 | contradiction 131 | intro g; rw[g] 132 | 133 | lemma char_eq_of_toByteList_prefix : List.IsPrefix (Char_toUTF8 c1) (Char_toUTF8 c2) → c1 = c2 := by 134 | intro g 135 | have g2: utf8Size c1 = utf8Size c2 := by 136 | unfold Char_toUTF8 Char_toUTF8_aux at g 137 | have i1: utf8Size c1 ≠ 0 := by apply Nat.ne_of_gt; apply utf8Size_pos 138 | have i2: utf8Size c2 ≠ 0 := by apply Nat.ne_of_gt; apply utf8Size_pos 139 | split at g ; contradiction ; split at g; contradiction 140 | simp only [succ_eq_add_one, le_refl, tsub_eq_zero_of_le, *] at g; 141 | exact Char_size_eq_of_firstbyte_eq (prefix_cons g).left 142 | have l1: (Char_toUTF8 c1).length = utf8Size c1 :=by apply Char_toUTF8_length 143 | have l2: (Char_toUTF8 c2).length = utf8Size c2 :=by apply Char_toUTF8_length 144 | have l3: (Char_toUTF8 c1).length = (Char_toUTF8 c2).length := by omega 145 | have ge : Char_toUTF8 c1 = Char_toUTF8 c2:= by apply prefix_eq_self g l3 146 | exact Char_toUTF8_eq_iff_eq.mp ge 147 | 148 | lemma prefix_iff_listByte_prefix : List.IsPrefix (Str_toUTF8 p) (Str_toUTF8 s) ↔ List.IsPrefix p s := by 149 | induction p generalizing s 150 | simp only [Str_toUTF8, _root_.nil_prefix] 151 | rename_i hp tp ind 152 | cases s 153 | simp only [Str_toUTF8, prefix_nil, append_eq_nil, not_false_eq_true, not_sym, iff_false, not_and] ; 154 | have : Char_toUTF8 hp ≠ [] := by apply Char_toUTF8_ne_nil 155 | simp only [this, not_false_eq_true, not_sym, false_implies] 156 | rename_i hs ts ; simp [Str_toUTF8] ; 157 | constructor 158 | intro gc 159 | have go:= prefix_append_or gc 160 | cases go; rename_i go; have go:= char_eq_of_toByteList_prefix go 161 | rw [go]; apply prefix_cons_mpr 162 | rw [go] at gc; 163 | have gc:= (List.prefix_append_right_inj (Char_toUTF8 hs)).mp gc 164 | simp only [ind.mp, gc] 165 | rename_i go; have go:= char_eq_of_toByteList_prefix go 166 | rw [go]; apply prefix_cons_mpr 167 | rw [go] at gc; 168 | have gc:= (List.prefix_append_right_inj (Char_toUTF8 hp)).mp gc 169 | simp only [ind.mp, gc] 170 | intro g ; have g:= prefix_cons g 171 | rw [g.left]; 172 | apply (prefix_append_right_inj (Char_toUTF8 hs)).mpr 173 | simp only [ind.mpr, g.right] 174 | 175 | lemma Str_toUTF8_append: Str_toUTF8 (s1 ++ s2) = Str_toUTF8 s1 ++ Str_toUTF8 s2 :=by 176 | induction s1 177 | simp only [nil_append, Str_toUTF8] 178 | rename_i h1 t1 ind 179 | simp only [Str_toUTF8, append_eq, ind, append_assoc] 180 | 181 | lemma Str_toUTF8_eq_iff_eq: Str_toUTF8 s1 = Str_toUTF8 s2 ↔ s1 = s2 :=by 182 | constructor 183 | induction s1 generalizing s2 184 | cases s2; simp only [imp_self] 185 | simp only [Str_toUTF8, nil_eq_append, Char_toUTF8_ne_nil, false_and, not_false_eq_true, not_sym, imp_self] 186 | rename_i h1 t1 ind 187 | cases s2 188 | simp only [Str_toUTF8, append_eq_nil, Char_toUTF8_ne_nil, false_and, not_false_eq_true, not_sym, imp_self] 189 | rename_i h2 t2 190 | simp only [Str_toUTF8, cons.injEq] 191 | intro g 192 | have g1: List.IsPrefix (Char_toUTF8 h1) (Char_toUTF8 h1 ++ Str_toUTF8 t1):=by simp only [prefix_append] 193 | have g2: List.IsPrefix (Char_toUTF8 h2) (Char_toUTF8 h1 ++ Str_toUTF8 t1):=by simp only [g, prefix_append] 194 | have gp:= List.prefix_or_prefix_of_prefix g1 g2 195 | have ge: h1 = h2 :=by 196 | cases gp; exact char_eq_of_toByteList_prefix (by assumption); symm; exact char_eq_of_toByteList_prefix (by assumption) 197 | simp only [ge, true_and]; simp only [ge, append_cancel_left_eq] at g 198 | exact ind g 199 | intro g; simp only [g] 200 | 201 | lemma Str_toUTF8_length: (Str_toUTF8 s).length = byteSize s:=by 202 | induction s 203 | simp only [Str_toUTF8, length_nil, byteSize] 204 | rename_i ind 205 | simp only [Str_toUTF8, length_append, Char_toUTF8_length, ind, byteSize] 206 | 207 | lemma Str_toUTF8_take_prefix: List.IsPrefix p s → (Str_toUTF8 s).take (byteSize p) = Str_toUTF8 p :=by 208 | intro g; rw[← @Str_toUTF8_length p, prefix_eq_take]; exact prefix_iff_listByte_prefix.mpr g 209 | -------------------------------------------------------------------------------- /lake-manifest.json: -------------------------------------------------------------------------------- 1 | {"version": "1.1.0", 2 | "packagesDir": ".lake/packages", 3 | "packages": [], 4 | "name": "«rust-lean-models»", 5 | "lakeDir": ".lake"} 6 | -------------------------------------------------------------------------------- /lakefile.toml: -------------------------------------------------------------------------------- 1 | name = "«rust-lean-models»" 2 | defaultTargets = ["RustLeanModels"] 3 | 4 | [leanOptions] 5 | pp.unicode.fun = true 6 | 7 | [[lean_lib]] 8 | name = "RustLeanModels" 9 | -------------------------------------------------------------------------------- /lean-toolchain: -------------------------------------------------------------------------------- 1 | leanprover/lean4:v4.11.0 2 | --------------------------------------------------------------------------------