{"id":"4d4ae307a70fc856","slug":"fibonacci-formalized-1-some-sums","trashed":false,"description":"","likes":14,"publish_level":"public","forks":1,"fork_of":null,"has_importers":false,"update_time":"2021-01-29T03:01:28.411Z","first_public_version":null,"paused_version":null,"publish_time":"2019-07-28T19:40:05.793Z","publish_version":2137,"latest_version":2137,"thumbnail":"dda7a3e44dc7e8d8fd28d185a2db4dbd0261b95ae53236befb847e14cd8938d0","default_thumbnail":"aa776dff8c649c3210161534e3586ce8b6e607f442574e257da0305396e59d02","roles":[],"sharing":null,"owner":{"id":"d2fd23101beb2a61","avatar_url":"https://avatars.observableusercontent.com/avatar/73f0dccc0b7fc5c845e6a52a58e01a2ae432cc529aaada0d10ab602137e603c0","login":"bryangingechen","name":"Bryan Gin-ge Chen","bio":"here to learn","home_url":"https://twitter.com/blockspins","type":"team","tier":"starter_2024"},"creator":{"id":"5c13a1a27cf52564","avatar_url":"https://avatars.observableusercontent.com/avatar/73f0dccc0b7fc5c845e6a52a58e01a2ae432cc529aaada0d10ab602137e603c0","login":"bryangingechen","name":"Bryan Gin-ge Chen","bio":"here to learn","home_url":"https://twitter.com/blockspins","tier":"public"},"authors":[{"id":"5c13a1a27cf52564","avatar_url":"https://avatars.observableusercontent.com/avatar/73f0dccc0b7fc5c845e6a52a58e01a2ae432cc529aaada0d10ab602137e603c0","name":"Bryan Gin-ge Chen","login":"bryangingechen","bio":"here to learn","home_url":"https://twitter.com/blockspins","tier":"public","approved":true,"description":""}],"collections":[{"id":"68b035771823d0b5","type":"public","slug":"lean","title":"Lean","description":"Notebooks related to the Lean theorem prover https://leanprover.github.io","update_time":"2019-12-14T08:32:41.501Z","pinned":false,"ordered":true,"custom_thumbnail":"e31ecdc4b6bd07378620dcddfaefecb887a104cb84645085ff32760cf4f3d2a4","default_thumbnail":"90ec3d3750f7d06c21ecc29f59b558cfe3ad9f18501b1ede75c7d05d0bd66774","thumbnail":"e31ecdc4b6bd07378620dcddfaefecb887a104cb84645085ff32760cf4f3d2a4","listing_count":10,"parent_collection_count":0,"owner":{"id":"d2fd23101beb2a61","avatar_url":"https://avatars.observableusercontent.com/avatar/73f0dccc0b7fc5c845e6a52a58e01a2ae432cc529aaada0d10ab602137e603c0","login":"bryangingechen","name":"Bryan Gin-ge Chen","bio":"here to learn","home_url":"https://twitter.com/blockspins","type":"team","tier":"starter_2024"}},{"id":"556a57fe7d41a95b","type":"public","slug":"meals","title":"Meals","description":"Long-form interactive writing / drawing; non-fictional modulo conjectures, open questions and the hopefully rare bug. Hopefully nourishing to your soul!","update_time":"2019-04-05T14:10:56.262Z","pinned":false,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"fd3394284958d119ee98e7720c9cc0c73cf40c9dda5b60abb5f811c6614df392","thumbnail":"fd3394284958d119ee98e7720c9cc0c73cf40c9dda5b60abb5f811c6614df392","listing_count":3,"parent_collection_count":0,"owner":{"id":"d2fd23101beb2a61","avatar_url":"https://avatars.observableusercontent.com/avatar/73f0dccc0b7fc5c845e6a52a58e01a2ae432cc529aaada0d10ab602137e603c0","login":"bryangingechen","name":"Bryan Gin-ge Chen","bio":"here to learn","home_url":"https://twitter.com/blockspins","type":"team","tier":"starter_2024"}}],"files":[],"comments":[],"commenting_lock":null,"suggestion_from":null,"suggestions_to":[],"version":2137,"title":"Fibonacci formalized 1: some sums","license":null,"copyright":"","nodes":[{"id":0,"value":"md`# Fibonacci formalized 1: some sums`","pinned":false,"mode":"js","data":null,"name":null},{"id":519,"value":"md`I like math. I like thinking about it and talking about it with like-minded friends. But I had to take a bunch of courses, do a lot of challenging reading and writing, and most importantly, learn from many good teachers before I was ready to appreciate the style of math that appears in most papers and books.\n\nHere's a crude analogy: written definitions, theorems and proofs are kind of like a very high-level source code for math, and it takes time and care to train one's brain to efficiently interpret this code. Correct proofs should in principle \"compile\" to a sequence of individually justifiable logical deductions, but in practice, many steps are left implicit per the writer's taste, and a reader needs confidence and experience to successfully fill these in. Furthermore, proofs frequently rely on definitions and results given elsewhere, so trying to understand an argument without sufficient prerequisites is like reading code for a program when you don't have access to the library functions being called. Given these difficulties, wouldn't it be nice to have a computer help out, just as we have fancy editors and IDEs assist us with other sorts of code?\n\nAs it happens, programming languages for formal proofs already exist. [Proof assistants](https://en.wikipedia.org/wiki/Proof_assistant) (also known as \"interactive theorem provers\") are computer programs that enable their users to write mathematical definitions, theorems, and proofs as code. This code is simultaneously checked for correctness, so that only theorems that have been formally verified are ultimately accepted into the system. Proof assistants frequently feature interfaces to text editors that assist in the writing of this code, including e.g. facilities for searching and applying previously proved results as well as tools for inspecting proofs, such as a \"goal\" display which displays the progress made in proving a statement. \n\nAdmittedly, these programs are still rather difficult to use – after all, you have to learn a new programming language to use a proof assistant (and sometimes more than one)! On the other hand, in addition to checking correctness and assisting searches and all that, they also enable you to interact with proofs in new and wonderful ways. Formalized proofs are in essence gigantic syntax trees which can be analyzed and visualized; see e.g. the picture produced by the [Lean perfectoid project](https://leanprover-community.github.io/lean-perfectoid-spaces/). Plus, there's a certain fun factor: poking my way towards a formal proof while the computer validates moves and returns the state really does feel a lot like playing a puzzle game (cf. [these comments by Mike Shulman on Coq](https://golem.ph.utexas.edu/category/2012/06/the_gamification_of_higher_cat.html) cited in Katherine Ye's slides linked below). \n\nWhile I don't believe that informal written math can or should be replaced by code, I do think that (some) future mathematical expositions might be integrated with interactive formalizations to the benefit of readers and authors alike. I'm not sure if those will be implemented using anything like currently extant proof assistants. However, it might still be interesting to experiment with integrating today's proof assistants into mathematical text. If nothing else, such an effort may make these programs more accessible and easier to teach and learn.\n\nIn this spirit, I've written up some simple mathematical proofs in a pair of Observable notebooks with their formalizations displayed in an integrated proof assistant ([Lean](http://leanprover.github.io/), using the editor from [this notebook](https://observablehq.com/@bryangingechen/hello-lean-prover)). Specifically, in Part 1 (this notebook), I prove three [Fibonacci number](https://en.wikipedia.org/wiki/Fibonacci_number) sum identities and (in [Part 2](https://observablehq.com/@bryangingechen/fibonacci-formalized-2-bees-and-cars)), I prove that certain objects are counted by Fibonacci numbers and make simple graphics from the Lean output. \n\nIf by chance you're already familiar with Lean and are just curious about how I wrote this document (and how you can too!), check out [the last section of this notebook](#sectionImplementation).\n\n*Thanks to the denizens of the [Lean prover Zulip chat](https://leanprover.zulipchat.com/) for answering lots of questions and helping out with proofs when I was getting started! Hop in if you're interested in all in Lean, questions at all levels are welcomed.*\n\n**Some inspiring links**:\n- Jeremy Avigad, [The Mechanization of Mathematics (pdf)](http://dx.doi.org/10.1090/noti1688), [[talk slides] (pdf)](https://www.andrew.cmu.edu/user/avigad/Talks/mechanization_talk.pdf),\n- Kevin Buzzard, [Formalising mathematics – a mathematician's viewpoint](https://xenaproject.wordpress.com/2018/09/22/formalising-mathematics-a-mathematicians-personal-viewpoint/) (and there are many other good posts on the Xena project blog),\n- Katherine Ye, [Proof assistants as a tool for thought [talk slides] (pdf)](http://www.cs.cmu.edu/~kqy/resources/coq_tools_for_thought.pdf).\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":1254,"value":"contents = md`## Contents\n\n**Part 1**:\n- [Fibonacci numbers](#sectionStart)\n- [Formulas 🧮](#sectionFormulas)\n  - [Sum of odd Fibonacci numbers](#sectionSumOdd)\n  - [Sum of even Fibonacci numbers](#sectionSumEven)\n  - [Sum of all Fibonacci numbers](#sectionSumAll)\n- [Implementation notes for Part 1](#sectionImplementation)\n\n[Part 2](https://observablehq.com/@bryangingechen/fibonacci-formalized-2-bees-and-cars):\n- [Bees 🐝](https://observablehq.com/@bryangingechen/fibonacci-formalized-2-bees-and-cars#sectionBees)\n- [Cars 🚗](https://observablehq.com/@bryangingechen/fibonacci-formalized-2-bees-and-cars#sectionCars)\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":518,"value":"sectionStart = md`## Fibonacci numbers\n\nLet's start the math with a definition of the Fibonacci numbers. The first two are 0 and 1 and then each number afterwards is defined to be the sum of the two preceding it in the sequence. Thus the Fibonacci sequence begins: \n\n${tex.block`0,1,1,2,3,5,8,13,21,34,55,89,\\dots`} \n\nIf ${tex`F_n`} denotes the ${tex`n`}th Fibonacci number, then we have the following recursive definition:\n\n${tex.block`F_n = \\begin{cases}\n0 & n=0,\\\\\n1 & n=1,\\\\\nF_{n-2} + F_{n-1} & n>1.\n\\end{cases}`}\n\nHere it is formalized in Lean:\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":29,"value":"viewof f0 = fibFormulasEditor(0, invalidation, { height: '175px' })","pinned":false,"mode":"js","data":null,"name":null},{"id":695,"value":"leanResources = md`The above text box is read-only, but you can still click around or hover over code to inspect it. The main things you will see when hovering around here are the [types](https://en.wikipedia.org/wiki/Type_theory) of certain expressions. Unfortunately, I won't give an actual tutorial for Lean in this notebook, only some hints about what the code you're reading means. I recommend looking at the books [Theorem Proving in Lean](https://leanprover.github.io/theorem_proving_in_lean/introduction.html) and [Logic & Proof](http://avigad.github.io/logic_and_proof/introduction.html) if you're interested in learning more about Lean! The former is a bit more like an actual tutorial but also a bit more advanced in terms of the background knowledge in logic required. The latter is actually a book on introductory mathematical logic and proofs in mathematics, but it has several nice chapters on Lean.\n\nThe first line tells Lean to import [a library file](https://github.com/leanprover-community/mathlib/blob/master/src/data/list/basic.lean) containing basic facts about lists. You can ignore the \"\\`@[reducible]\\`\" for now – suffice it to say that it's an \"attribute\" which tells Lean to unfold this definition wherever possible.\n\nThe colon in: \n${leanHL`def fibonacci : ℕ → ℕ`}\n\nis Lean syntax for a type annotation. This particular line declares a new function \\`fibonacci\\` of type \\`ℕ → ℕ\\`, i.e. a function from the nats (natural numbers) to the nats. The remainder of the definition uses Lean's [pattern-matching syntax](https://leanprover.github.io/theorem_proving_in_lean/induction_and_recursion.html#pattern-matching) (which you can think of as kind of like a \\`switch ... case\\` statement) to complete the definition by providing a function of that type.\n\nExplicitly, when \\`fibonacci\\` is applied to a nat, Lean looks to match it to one of 3 cases (exactly as in the recursive formula above):\n- Case \\`0\\`:\n${leanHL`| 0 := 0`}\n\n  (Read as: \\`fibonacci 0\\` is defined to be (\\`:=\\`) \\`0\\`.)\n\n- Case \\`1\\`:\n${leanHL`| 1 := 1`}\n\n- Case \\`(n+2)\\`:\n${leanHL`| (n+2) := fibonacci n + fibonacci (n+1)`}\n\nNote that Lean's equation compiler is smart enough to recognize that:\n- every nat is covered by the above cases, and\n- even though \\`fibonacci\\` references itself in the \\`(n+2)\\` case, the function arguments strictly decrease, so that the recursion will eventually terminate.\n\n(If you try to define a function on the natural numbers which breaks these rules, Lean will throw an error.)\n\nThe final line of the box above, \\`open nat list\\`, lets us write abbreviations like \\`add_comm\\` and \\`map\\` for \\`nat.add_comm\\` and \\`list.map\\`.\n\nIn this notebook, the main way in which we'll be running Lean code is using the \\`#eval\\` command. Roughly speaking, when the Lean server encounters \\`#eval\\`, it attempts to evaluate the following expression (see [this chapter of TPiL](https://leanprover.github.io/theorem_proving_in_lean/axioms_and_computation.html) for more details) and then returns the result as an \\`info\\` message. Below are some \\`#eval\\` statements using \\`fibonacci\\` – the following editor is not read-only, so feel free to experiment by changing the numbers or making other edits (the Reset button will bring you back to the initial state):`","pinned":false,"mode":"js","data":null,"name":null},{"id":81,"value":"viewof fEval = leanEditor({\n  docArray: [    \n    `import fib\nopen nat list`,\n    `#eval fibonacci 5\n#eval (range 10).map fibonacci`\n  ],\n  docIndex: 1,\n  fileName: 'fib_formula_eval.lean',\n  displayMode: 1,\n  lineNumbers: false,\n  height: '200px',\n  header: html``,\n}, invalidation)","pinned":false,"mode":"js","data":null,"name":null},{"id":706,"value":"md`Note that function application doesn't require parentheses; the expression \\`fibonacci 5\\` evaluates the function \\`fibonacci\\` at 5, similarly, \\`range 10\\` evaluates the \\`range\\` function at 10.\n\nIn general, given a function \\`f\\` of type \\`A → B\\` and an expression \\`a\\` of type \\`A\\`, \\`f a\\` is of type \\`B\\`.\n\nThe second \\`#eval\\` statement deserves a little bit of explanation. Try hovering over \\`range\\` and you'll see that it has type \\`ℕ → list ℕ\\`. This means that it's a function from nats to lists of nats. In particular, \\`range 10\\` evaluates to a list containing the numbers from 0 to 9. \n\nFinally, the expression \\`(range 10).map fibonacci\\` uses a certain shorthand which we'll see much more of below. The function \\`list.map\\` is under the \\`list\\` namespace and has type \\`(α → β) → list α → list β\\` (in words, it takes a function of type \\`α → β\\`, a list whose elements are of type \\`α\\` and returns a list of elements of type \\`β\\`). If \\`xs\\` has type \\`list α\\` and \\`f\\` has type \\`α → β\\`, then \\`xs.map f\\` is short for \\`list.map xs f\\`. Thus  \\`(range 10).map fibonacci\\` is really \\`map fibonacci (range 10)\\`, which is an application of the \\`fibonacci\\` function to each of the elements of the list \\`range 10\\`. \n\nIn general, given a function of type \\`foo.bar\\` with a parameter of type \\`foo\\` and a term \\`t : foo\\`, we can write \\`t.bar\\` and Lean will interpret this as \\`foo.bar\\` with \\`t\\` passed as an argument in the appropriate place.\n\nAs an aside, note that we can parse the messages from Lean and use them in Observable cells:`","pinned":false,"mode":"js","data":null,"name":null},{"id":438,"value":"parseLeanMsgs(fEval.msgs)","pinned":true,"mode":"js","data":null,"name":null},{"id":431,"value":"sectionFormulas = md`## Formulas 🧮\n\nI think I first learned about Fibonacci numbers in middle school. Middle school algebra was all about cool formulas, and I remember having a blast proving cool sum formulas (likely due in no small part to the joy of scrawling ${tex`\\Sigma`}'s and other mysterious symbols in my homework). In this section, let's see if we can teach Lean some sums. Specifically, these three:\n\n${tex.block`\\begin{aligned}\nF_0+F_1+F_2+\\cdots+F_n &= F_{n+2} - 1,\\\\\nF_1 + F_3 + F_5 + \\cdots + F_{2n-1} &= F_{2n},\\\\\nF_0 + F_2 + F_4 + \\cdots + F_{2n} &= F_{2n+1} - 1.\n\\end{aligned}`}\n\nIt turns out to be convenient later to work with sums that go to ${tex`n-1`} (because \\`range n\\` only contains numbers from \\`0\\` to \\`n-1\\`); with this choice, the sum identities above can be written in more compact notation:\n${tex.block`\\begin{aligned}\n1+\\sum_{m=0}^{n-1}F_m&= F_{n+1},\\\\\n\\sum_{m=0}^{n-1}F_{2m+1} &= F_{2n},\\\\\n1+\\sum_{m=0}^{n-1}F_{2m}&= F_{2n-1}.\n\\end{aligned}`}\n\nWe take the convention that sums from 0 to -1 (which appear when ${tex`n=0`}) are zero (since they are sums over an empty set).\n\nWe'll prove the first two identities for all ${tex`n\\in\\mathbb{N}`} and the last one for nats ${tex`n>0`} (since we haven't defined ${tex`F_{-1}`}).\n\nThe next code block contains the following definitions in Lean:\n- \\`fib_sum n\\` := ${tex`\\sum_{m=0}^{n-1}F_m,`}\n- \\`fib_odd_sum n\\` := ${tex`\\sum_{m=0}^{n-1}F_{2m+1},`}\n- \\`fib_even_sum n\\` := ${tex`\\sum_{m=0}^{n-1}F_{2m}.`}\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":381,"value":"viewof f1 = fibFormulasEditor(1, invalidation, { height: '225px' })","pinned":false,"mode":"js","data":null,"name":null},{"id":733,"value":"md`The \\`.sum\\` notations make use of the shorthand described above for \\`list.sum\\` (note that the parenthesized expressions that \\`.sum\\` is applied to are all of type \\`list\\`).\n\nIn the second and third definitions above, the \\`λ\\`'s are used to define [anonymous functions](https://en.wikipedia.org/wiki/Anonymous_function) which are then passed to the \\`list.map\\` function.\n\nGiven these definitions, we can now check the formulas above in a few cases:`","pinned":false,"mode":"js","data":null,"name":null},{"id":565,"value":"viewof fSumEval = leanEditor({\n  docArray: [\n`import fib\nopen nat list`,\n    `#eval (range 10).map fib_sum\n#eval (range 10).map (λ m, fibonacci (m+1) - 1)\n\n#eval (range 10).map fib_odd_sum\n#eval (range 10).map (λ m, fibonacci (2*m))\n\n#eval (range 10).map fib_even_sum\n#eval (range 10).map (λ m, fibonacci (2*m-1) - 1)`\n  ],\n  docIndex: 1,\n  fileName: 'fib_formula_sum_eval1.lean',\n  displayMode: 1,\n  lineNumbers: false,\n  height: '275px',\n  header: html``,\n}, invalidation)","pinned":false,"mode":"js","data":null,"name":null},{"id":1672,"value":"md`🤔 You might have noticed something strange about the last \\`#eval\\` statement. When ${tex`m=0`}, what should the expression \\`fibonacci (2*m-1) - 1\\` evaluate to? Try typing \\`#eval 0-1\\` above and you might be surprised. The explanation is that subtraction on Lean's nats is a [\"total function\"](https://softwareengineering.stackexchange.com/questions/334874/in-the-context-of-functional-programming-what-are-total-functions-and-partia) and so it *must* return another nat. Thus when \\`a < b\\`, \\`a - b\\` is defined to be \\`0\\`. In any case, as mentioned above, we will only be proving things about this function when \\`m > 0\\`.`","pinned":false,"mode":"js","data":null,"name":null},{"id":1674,"value":"sectionSumOdd = md`### Sum of odd Fibonacci numbers`","pinned":false,"mode":"js","data":null,"name":null},{"id":966,"value":"md`Before we look at the formal proofs, let's quickly prove one of these \"informally\". The basic idea in all cases is to proceed by [the principle of induction](https://en.wikipedia.org/wiki/Mathematical_induction). To prove any proposition ${tex`P`} for all natural numbers ${tex`n`}, we:\n- first prove that ${tex`P`} holds when ${tex`n=0`}, \n- show that if ${tex`P`} holds for ${tex`n`}, it also holds for ${tex`n+1`}. \n\nFor example, consider the identity for the sum of odd Fibonacci numbers:\n\n**Theorem**: For all ${tex`n \\in \\mathbb{N}`},\n${tex.block`\\sum_{m=0}^{n-1}F_{2m+1} = F_{2n}.`} \n\n*Proof*: By the principle of induction, we consider two cases:\n- When ${tex`n=0`}, the empty sum on the left hand side is zero, per our convention. The right hand side is ${tex`F_{0}=0`}, so the identity holds in this case.\n- Now suppose the identity holds for ${tex`n`}. We want to prove the ${tex`n+1`} case of the identity:\n${tex.block`\\sum_{m=0}^{n}F_{2m+1} = F_{2(n+1)}.`} \n\n  To do so, we manipulate the sum on the left hand side until it becomes the right hand side:\n${tex.block`\\begin{aligned}\n\\sum_{m=0}^nF_{2m+1} =& \\sum_{m=0}^{n-1}F_{2m+1} + F_{2n+1}&\\text{by extracting the last summand}\\\\\n=& F_{2n} + F_{2n+1}&\\text{by the inductive hypothesis}\\\\\n=& F_{2n+2}&\\text{by the definition of $F_{m}$}\\\\\n=& F_{2(n+1).}\n\\end{aligned}`}\n\nThat concludes the proof! ${tex`\\square`}\n\nThe text box below shows a formalization of this proof in Lean. By default, Lean is silent when given a correct proof (\"if it compiles, it's correct\"). [Writing proofs in Lean is thus often a matter of eliminating \"squiggly lines\", which indicate errors.] As with the definitions shown above, Lean will provide info when you hover or click around. You'll see that Lean provides \"goal\" info in certain parts of the proof. Click the prev and next buttons below to navigate through an explanation of the code:`","pinned":false,"mode":"js","data":null,"name":null},{"id":224,"value":"viewof f2 = fibFormulasEditor(2, invalidation, { height: '225px', footer: stepEditor(steps) })","pinned":false,"mode":"js","data":null,"name":null},{"id":1680,"value":"sectionSumEven = md`### Sum of even Fibonacci numbers:`","pinned":false,"mode":"js","data":null,"name":null},{"id":1057,"value":"md`The next identity is for the sum of even Fibonacci numbers:\n\n**Theorem**: For all positive natural numbers ${tex`n`}:\n\n${tex.block`1 + \\sum_{m=0}^{n-1}F_{2m} = F_{2n-1}.`}\n\n*Proof*: The basic idea is still to extract the largest term of the sum and then use the induction hypothesis + definition of Fibonacci numbers. The only wrinkle is that our induction starts at 1 instead of 0 (since we only want to prove this for ${tex`n>0`}):\n- ${tex`n=1`}: the LHS is ${tex`1 + F_0 = 1`} and the RHS is ${tex`F_{2\\times1-1} = F_1 = 1`}.\n- Assuming the identity holds for ${tex`n`}, we show ${tex`1 + \\sum_{m=0}^{n}F_{2m} = F_{2n+1},`} by manipulating the LHS:\n${tex.block`\\begin{aligned}\n1 + \\sum_{m=0}^{n}F_{2m} &= 1 + \\sum_{m=0}^{n-1}F_{2m} + F_{2n}\\\\\n&= F_{2n-1} + F_{2n}\\\\\n&= F_{2n+1}.\n\\end{aligned}`}\n${tex`\\square`}\n\nThe Lean proof looks like this:\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":226,"value":"viewof f3 = fibFormulasEditor(3, invalidation, { height: '375px' })","pinned":false,"mode":"js","data":null,"name":null},{"id":1090,"value":"md`The type of \\`fib_even_sum_eq\\` is a little more complicated due to the condition \\`n > 0\\`:\n\n${leanHL`∀ {n : ℕ} (h : n > 0), fib_even_sum n + 1 = fibonacci (2*n - 1)`}\n\nIn words: \"For all nats \\`n\\`, and all proofs \\`h\\` that \\`n > 0\\`, we have the equality \\`fib_even_sum n + 1 = fibonacci (2*n - 1)\\`.\"\n\nPattern-matching happens on both \\`n\\` and \\`h\\`, hence we have 2 parameters in each case:\n\n- Case 0:\n${leanHL`| 0 h := (gt_irrefl 0 h).elim`}\n\n  In Lean, we have to give an argument to rule out the \\`n = 0\\` case. This is a little silly so feel free to skip to the next case. Here, the hypothesis \\`h\\` would have to be a proof that \\`0 > 0\\`. This, of course, is impossible and leads to a contradiction (\\`gt_irrefl 0 h : false\\`), so Lean lets us close the goal with \\`.elim\\` (short for \\`false.elim\\` which is [the principle of explosion](https://en.wikipedia.org/wiki/Principle_of_explosion)).\n\n- Case 1:\n${leanHL`| 1 _ := rfl`}\n\n  Just as in the base case of the previous theorem, Lean is able to recognize that the LHS and RHS are equal, so \\`rfl\\` suffices. I've used an underscore for the proof of \\`1 > 0\\` as a way of indicating that it's unused. (Outside of pattern-matching, an underscore is a special keyword which tells Lean to try to infer some expression to replace it.)\n\n- Case \\`n+2\\`:\n${leanHL`| (n+2) _ :=`}\n\n  The \\`have\\` syntax tells Lean that we want to prove a side result first and add it to the context for this proof. While we won't see it much in these pages, this is a good way of storing intermediate results in a long proof:\n${leanHL`have H : fib_even_sum (n+1) + 1 = fibonacci (2*(n+1) - 1) :=\n`}\n\n  This line says that \\`H\\` is a proof of the equality \\`fib_even_sum (n+1) + 1 = fibonacci (2*(n+1) - 1)\\`. It's defined to be an application of the inductive hypothesis (named \\`fib_even_sum_eq\\`) to a proof that \\`n+1\\` is positive (\\`succ_pos n\\`):\n${leanHL`  fib_even_sum_eq (succ_pos n),`}\n\n  The \\`begin ... end\\` block also tells Lean to enter tactic mode. (The difference with \\`by\\` is that we can put multiple commands between \\`begin\\` and \\`end\\`.) The tactics used are \\`rw\\`, \\`change\\` and \\`rw\\` again. I leave it to you to inspect the rewrites, which are mostly straightforward (remember that you can check the type of the rewrite lemma by hovering over it). The \\`change\\` tactic lets us replace the goal with something definitionally equivalent; in this case, I used it to avoid doing some silly arithmetic with 1's and 2's.\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":1683,"value":"sectionSumAll = md`### Sum of Fibonacci numbers:`","pinned":false,"mode":"js","data":null,"name":null},{"id":1215,"value":"md`Here's the final Fibonacci sum identity. I will omit the informal proof, but it goes almost exactly as the ones above (use induction, split the sum, apply the definition of Fibonacci, etc.). [We could also prove it from the two above identities, but that turns out to require more manipulation.]`","pinned":false,"mode":"js","data":null,"name":null},{"id":228,"value":"viewof f4 = fibFormulasEditor(4, invalidation, { height: '200px' })","pinned":false,"mode":"js","data":null,"name":null},{"id":1218,"value":"md`The above Lean proof makes use of the \\`simp\\` tactic. Roughly speaking, it attempts to simplify the goal by repeatedly rewriting it using its library of known facts (anything in the imported libraries which are tagged with the \\`simp\\` attribute as well as anything included in the list that follows). In this case, it saves us from writing explicitly a long list of lemmas in a \\`rw\\` statement.\n\nProofs using more powerful tactics tend to be more concise and hence more opaque (though Lean does have capabilities for \"tracing\" the behavior of tactics). \n\n### *See [Part 2](https://observablehq.com/@bryangingechen/fibonacci-formalized-2-bees-and-cars) for formalizations of certain objects counted by Fibonacci numbers (including some graphics driven by Lean code)*.`","pinned":false,"mode":"js","data":null,"name":null},{"id":145,"value":"sectionImplementation = md`## Implementation notes\n\n*This section is primarily written for Lean users who aren't familiar with Observable notebooks. If you're more interested in Lean, see [the resources linked above](#leanResources).*\n\nThis document is an [Observable notebook](https://observablehq.com/@observablehq/five-minute-introduction) (see also: the [\"User manual\"](https://observablehq.com/@observablehq/user-manual)). You can publish notebooks just like this one if you sign in with your Github, Twitter or Google account! While these notebooks resemble Jupyter or Mathematica notebooks in that they are made up of \"cells\", they actually [run more like spreadsheets](https://observablehq.com/@observablehq/how-observable-runs), since cells are **automatically re-evaluated** when they are edited or when any of their dependencies change. This is in contrast to other notebook systems where cells are either evaluated manually, or are made to run once from top to bottom. \n\nA great feature of Observable notebooks is that all the code is easily available: **click to the left of any \"unpinned\" cell to display its source**. While everything is actually written in JavaScript ([kind of](https://observablehq.com/@observablehq/observables-not-javascript)), most of the content, including this cell, is actually markdown text, wrapped in an \\`md\\` \"tag function\". The equations above are written in LaTeX and included in the markdown using the \\`tex\\` tag function. See [\"Introduction to HTML\"](https://observablehq.com/@observablehq/introduction-to-html) for details.\n\nFinally, notebooks aren't restricted to the \\`observablehq.com\\` platform – they can be easily downloaded and embedded on other sites: see [this tutorial](https://observablehq.com/@observablehq/downloading-and-embedding-notebooks).\n\nThe code cells in the rest of this section are dependencies of the Lean editor cells above. I've included some explanation in the surrounding text below.`","pinned":false,"mode":"js","data":null,"name":null},{"id":1760,"value":"md`The Lean editor cells above are created by the functions \\`leanEditor\\` and \\`leanEditorFrom\\` from [another notebook](https://observablehq.com/@bryangingechen/hello-lean-prover#docArrayDoc). To use them here, we have to \"\\`import\\`\" them into this notebook with the following command (which also imports several other useful functions). We override the \\`transportOpts\\` so that we can load a custom ZIP bundle of Lean files (described below).`","pinned":false,"mode":"js","data":null,"name":null},{"id":4,"value":"import {leanEditor, leanEditorFrom, leanControl, leanHL, parseLeanMsgs} with {transportOpts} from '@bryangingechen/hello-lean-prover'","pinned":true,"mode":"js","data":null,"name":null},{"id":1784,"value":"md`Uncomment the following cell for a form that lets you reset the Lean server (useful for debugging):`","pinned":false,"mode":"js","data":null,"name":null},{"id":125,"value":"// leanControl","pinned":false,"mode":"js","data":null,"name":null},{"id":1800,"value":"md`Now, a word on how I prepared the Lean code used in this notebook. I first wrote a version of the Lean proofs in VS Code using the [\\`vscode-lean\\`](https://github.com/leanprover/vscode-lean) extension. Then I copied it into a cell in this notebook and modified it as I wrote the surrounding text.\n\nBy default, the Lean editor notebook loads a ZIP bundle containing only the core Lean library. However, this means that you can't immediately work with Lean files that build off of the Lean community standard library [mathlib](https://github.com/leanprover-community/mathlib).\n\nFor this reason, when I first started experimenting with my Lean code in this notebook, I used the following to import a Lean editor that loads a ZIP bundle containing all files in \\`mathlib\\`:\n\\`\\`\\`js\nimport {leanEditor, leanEditorFrom, leanControl, leanHL, parseLeanMsgs} \nwith {leanLibName} from '@bryangingechen/hello-lean-prover'\n\\`\\`\\`\nThe \\`with {leanLibName}\\` syntax refers to another cell that I had in this notebook:\n\\`\\`\\`js\nleanLibName = 'library'\n\\`\\`\\`\nwhich replaces [a certain cell](https://observablehq.com/@bryangingechen/hello-lean-prover#leanLibName) that is used in generating the URL to the Lean server's ZIP bundle. While this was a fine solution for personal use, the ZIP bundle with all of mathlib is ~19 MB, which is way too large to ask a casual reader to download. Fortunately, it's possible to use a much smaller ZIP file, at the cost of doing a bit more preparation.\n\nSo after I finalized the Lean code in this notebook, I copied it back to my computer and used \\`leanpkg\\` to make a new Lean package (see [\\`lean-fibonacci\\`](https://github.com/bryangingechen/lean-fibonacci)). I then used the \\`mk_library.py\\` script from my fork of the [\\`lean-web-editor\\`](https://github.com/bryangingechen/lean-web-editor) project (see the instructions in the README there) to prepare the much smaller (~3 MB) \\`libfib.zip\\`, which contains only the files needed for my code to work.\n\nFinally, I hosted \\`libfib.zip\\` in my personal \\`github.io\\` repository and then changed the import statement to the one currently used above. The \"\\`with {transportOpts}\\`\" syntax replaces the [\\`transportOpts\\` cell in the Lean editor notebook](https://observablehq.com/@bryangingechen/hello-lean-prover#transportOpts) with this:`","pinned":false,"mode":"js","data":null,"name":null},{"id":1767,"value":"transportOpts = {\n  const prefix = 'https://bryangingechen.github.io/lean/lean-web-editor/';\n  return {\n    javascript: prefix+'lean_js_js.js', // Lean server compiled to asm.js with Emscripten (won't be downloaded or used unless your browser doesn't support WASM)\n    webassemblyJs: prefix+'lean_js_wasm.js', // JS code generated by Emscripten to call...\n    webassemblyWasm: prefix+'lean_js_wasm.wasm', // Lean server compiled to WASM by Emscripten\n    libraryZip: prefix+'libfib.zip', // bundle of .olean files\n  };\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":1792,"value":"md`There are two types of Lean editors in this notebook, read-only editors, which are labeled with \"\\`fib_formulas.lean\\` (X/5)\" and non-read-only editors, which let the reader experiment with \\`#eval\\` statements.\n\nThe read-only editors contain parts of the following array, which is generated by [splitting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split) a string containing the first part of the Lean code in the \\`lean-fibonacci\\` repository:`","pinned":false,"mode":"js","data":null,"name":null},{"id":73,"value":"fibFormulas = String.raw`import data.list.range\n\n@[reducible]\ndef fibonacci : ℕ → ℕ\n| 0 := 0\n| 1 := 1\n| (n+2) := fibonacci n + fibonacci (n+1)\n\nopen nat list\n---\n@[reducible]\ndef fib_sum (n : ℕ) : ℕ :=\n((range n).map fibonacci).sum\n\n@[reducible]\ndef fib_odd_sum (n : ℕ) : ℕ :=\n((range n).map (λ m, fibonacci (2*m + 1))).sum\n\n@[reducible]\ndef fib_even_sum (n : ℕ) : ℕ :=\n((range n).map (λ m, fibonacci (2*m))).sum\n---\ntheorem fib_odd_sum_eq : ∀ (n : ℕ),\n  fib_odd_sum n = fibonacci (2*n)\n| 0 := rfl\n| (n+1) := by rw [fib_odd_sum,\n  sum_range_succ,\n  ←fib_odd_sum,\n  fib_odd_sum_eq,\n  mul_add,\n  mul_one,\n  fibonacci]\n---\ntheorem fib_even_sum_eq : ∀ {n : ℕ} (h : n > 0),\n  fib_even_sum n + 1 = fibonacci (2*n - 1)\n| 0 h := (gt_irrefl 0 h).elim\n| 1 _ := rfl\n| (n+2) _ :=\nhave H : fib_even_sum (n+1) + 1 = fibonacci (2*(n+1) - 1) :=\n  fib_even_sum_eq (succ_pos n),\nbegin\n  rw [fib_even_sum,\n    sum_range_succ,\n    ←fib_even_sum,\n    add_right_comm,\n    H,\n    mul_add,\n    mul_one,\n    mul_add],\n  change fibonacci (2*n + 1) + fibonacci (2*n + 1 + 1) = \n    fibonacci (2*n + 1 + 2),\n  rw [←fibonacci],\nend\n---\ntheorem fib_sum_eq : ∀ (n : ℕ),\n  fib_sum n + 1 = fibonacci (n+1)\n| 0 := rfl\n| (n+1) :=\nbegin\n  rw [fibonacci,\n    ←fib_sum_eq n],\n  simp [range_succ, fib_sum,\n    add_left_comm, add_comm],\nend`.split('\\n---\\n')","pinned":false,"mode":"js","data":null,"name":null},{"id":1805,"value":"md`I use this array to create the following \"editor factory\" function by passing an options object to \\`leanEditorFrom\\` with \\`fibFormulas\\` as the \\`docArray\\` field.\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":97,"value":"fibFormulasEditor = leanEditorFrom({\n  docArray: fibFormulas,\n  fileName: 'fib_formulas.lean',\n  readOnly: true,\n  lineNumbers: false,\n  height: 150,\n})","pinned":false,"mode":"js","data":null,"name":null},{"id":1838,"value":"md`As an example, here's the source of [the read-only cell that shows part 4/5](#f3):\n\\`\\`\\`js\nviewof f3 = fibFormulasEditor(3, invalidation, { height: 375 })\n\\`\\`\\`\n\nHere and elsewhere, you'll see [\\`invalidation\\`](https://observablehq.com/@mbostock/disposing-content) as an argument to various functions that create editors. This is a special [\"Promise\" object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) that is defined by Observable's runtime which resolves when a cell is re-run. If you're not familiar with promises, you can just think of this as something that helps ensure that the editor cleans up resources when it's destroyed.\n\nThe \\`viewof f3\\` part just names the cell and its output. While the value \"\\`f3\\`\" of this particular editor is not actually used elsewhere in the notebook, naming cells also creates anchors that we can then link to, as I did just above.`","pinned":false,"mode":"js","data":null,"name":null},{"id":2026,"value":"md`[One of the read-only editors](#f2) has a special footer element with buttons that step forwards / backwards through a multi-step explanation. This is implemented using the \\`stepEditor\\` function from [this notebook](https://observablehq.com/@bryangingechen/stepping-through-currying-in-lean).`","pinned":false,"mode":"js","data":null,"name":null},{"id":1930,"value":"import {stepEditor} from '@bryangingechen/stepping-through-currying-in-lean'","pinned":false,"mode":"js","data":null,"name":null},{"id":2033,"value":"md`For convenience, here's what the source of that editor cell looks like:\n\\`\\`\\`js\nviewof f2 = fibFormulasEditor(2, invalidation, { height: 225, footer: stepEditor(steps) })\n\\`\\`\\`\nThe input to \\`stepEditor\\` is the following array \\`steps\\` whose entries are objects describing the state of the footer and the highlighting in the editor in each \"step\". Here I only use two fields of each state object:\n- \\`desc\\` contains an HTML element to be displayed in the footer (using the \\`md\\` function), and \n- \\`marks\\` contains an array of CodeMirror ranges to highlight. \n\nSee the [\\`stepEditor\\` notebook](https://observablehq.com/@bryangingechen/stepping-through-currying-in-lean) for full documentation:`","pinned":false,"mode":"js","data":null,"name":null},{"id":1937,"value":"steps = [\n  {\n    desc: md`Let's go over this proof carefully. First, we have <span class='leanmark'>the declaration of the theorem</span>:\n\n${leanHL`theorem fib_odd_sum_eq : ∀ (n : ℕ),\n  fib_odd_sum n = fibonacci (2*n)`}\n\nThe \\`theorem\\` declaration is like the function declaration \\`def\\`, except that it's intended for propositions (which can't be \\`#eval\\`'d). We'll see \\`lemma\\` later, which does exactly the same thing. The name of the theorem is \\`fib_odd_sum_eq\\` and as before, the stuff after the colon tells us the type of the theorem. In words, it says \"For all nats \\`n\\`, \\`fib_odd_sum n = fibonacci (2*n)\\`\".\n\nHere we could digress into a very interesting discussion on dependent type theory and the [Curry-Howard / propositions-as-types correspondence](https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence). For our purposes, the following points are most relevant: just as a function is passed arguments of a particular data type and returns a value of some other data type, a theorem can be viewed as a function from its hypotheses (in this case, we're given a nat \\`n\\`) to its conclusion (\\`fib_odd_sum n = fibonacci (2*n)\\`). Thus in Lean, giving a valid proof of a theorem is like supplying a definition of a function that is consistent with its declared type.`,\n    marks: [{from: {line:0, ch:0}, to: {line:1, ch:33}}],\n  },\n  {\n    desc: md`As we saw above, we can define functions of the natural numbers in Lean via pattern matching. We can also use this syntax to prove theorems about the natural numbers. Thus the proof is structured around pattern matching on \\`n\\`; note how the two cases <span class='leanmark'>\\`0\\`</span> and <span class='leanmark'>\\`(n+1)\\`</span> resemble the cases in the induction in our informal proof.`,\n    marks: [{from: {line:2, ch:0}, to: {line:2, ch:3}},\n            {from: {line:3, ch:0}, to: {line:3, ch:7}},]\n  },\n  {\n    desc: md`<span class='leanmark'>Case 0</span>:\n${leanHL`| 0 := rfl`} \n  The proof in this case is \\`rfl\\` (which stands for \"reflexivity\"). It proves a goal of the form \\`a = a\\`. The fact that this succeeds means that Lean has confirmed that the two sides of the equality become identical after substituting \\`n = 0\\` and unfolding all definitions (in this case, \\`fib_odd_sum 0 = 0 = fibonacci (2 * 0)\\`).`,\n    marks: [{from: {line:2, ch:0}, to: {line:2, ch:10}}],\n  },\n  {\n    desc: md`<span class='leanmark'>Case \\`n+1\\`</span>:\n${leanHL`| (n+1) := by rw [...]`} \n\n⚠️ First, an important point about Lean's pattern-matching syntax: the \\`n\\` in \\`(n+1)\\` is **different** from the \\`n\\` in the theorem's type. You can think of the pattern \\`(n+1)\\` as introducing a new variable \\`n\\` that [**mask**](https://en.wikipedia.org/wiki/Variable_shadowing) the matched variable \\`n\\` in the theorem's type. It's probably best practice to give these variables different names to avoid confusion, but you'll see here and below that I didn't bother...\n\nThis case is proved by going into [\"tactic mode\"](https://leanprover.github.io/theorem_proving_in_lean/tactics.html) (with \\`by\\`) and then using the \\`rw\\` (\"rewrite\") tactic with a list (enclosed in square brackets) of rewriting lemmas. \n`,\n    marks: [{from: {line:3, ch:0}, to: {line:3, ch:17}}],\n  },\n  {\n    desc: md`You can inspect the \"tactic state\" for code within tactic blocks by placing the cursor somewhere or hovering the mouse pointer. Start out by placing your cursor or mouse pointer after the <span class='leanmark'>\\`by\\` and before the end of the first item in the list</span>. You will see something like this in the infoview or a popup:\n${html`<div style=\"border: 1px solid; padding: 1ex; max-width: 640px\">\n<div class='info-header'>24:15: goal</div>\n<pre>\nfib_odd_sum_eq : ∀ (n : ℕ), fib_odd_sum n = fibonacci (2 * n),\nn : ℕ\n⊢ fib_odd_sum (n + 1) = fibonacci (2 * (n + 1))\n</pre>\n</div>`}\n\nThis is a display of the current context where a list of hypotheses is shown before the [\"turnstile\"](https://en.wikipedia.org/wiki/Turnstile_(symbol) ⊢ and the desired goal is shown after it. Here the hypotheses are:\n- the inductive hypothesis \\`fib_odd_sum_eq : ∀ (n : ℕ), fib_odd_sum n = fibonacci (2 * n)\\`, and\n- \\`n\\` is a nat.\n\nAnd the goal is:\n- \\`⊢  fib_odd_sum (n + 1) = fibonacci (2 * (n + 1))\\`. \n\nWhile the inductive hypothesis looks like it proves this already (it says \\`∀ (n : ℕ)\\`, after all), if you try to use \\`fib_odd_sum_eq (n+1)\\`, Lean will notice that you're trying to cheat and spit out an error like \"failed to prove recursive application is decreasing\".\n\nTry placing your cursor before and after each of the elements of the rewrite list to see how the goal gets rewritten. [In this notebook, I've placed each rewrite on a separate line for ease of inspection.]\n  \nI'll go through the first three rewrites in the next few steps.`,\n    marks: [{from: {line:3, ch:13}, to: {line:3, ch:28}}],\n  },\n  {\n    desc: md`The first rewrite uses the definition of <span class='leanmark'>\\`fib_odd_sum\\`</span> to transform the original goal:\n${leanHL`⊢ fib_odd_sum (n + 1) = fibonacci (2 * (n + 1))`}\n\nto the goal:\n\n${leanHL`⊢ sum (map (λ (m : ℕ), fibonacci (2 * m + 1)) (range (n + 1))) = \n  fibonacci (2 * (n + 1))`}\n\nReminder: you can move the cursor back and forth between the last two letters of the rewrite lemma to see the goal change back and forth.`,\n    marks: [{from: {line:3, ch:18}, to: {line:3, ch:29}}],\n  },\n  {\n    desc: md`The second rewrite lemma <span class='leanmark'>\\`sum_range_succ\\`</span> has type \n\\`\\`\\`\n∀ {α : Type} [_inst_1 : add_monoid α] (f : ℕ → α) (n : ℕ),\n  sum (map f (range (succ n))) = sum (map f (range n)) + f n.\n\\`\\`\\` \n\nThis looks hairy, but just pay attention to the last bit: \\`sum (map f (range (succ n))) = sum (map f (range n)) + f n\\`. Note that \\`succ\\` stands for \"successor\", i.e. the \"+1\" function. In words, this says that the sum of a function \\`f\\` over \\`range (n+1)\\` is equal to the sum over \\`range n\\` plus \\`f n\\`. If we take \\`f = (λ (m : ℕ), fibonacci (2 * m + 1))\\`, then this is precisely the \"extracting the last summand\" step above. The goal becomes:\n\n${leanHL`⊢ sum (map (λ (m : ℕ), fibonacci (2 * m + 1)) (range n)) +\n  fibonacci (2 * n + 1) = fibonacci (2 * (n + 1))`}`,\n    marks: [{from: {line:4, ch:2}, to: {line:4, ch:16}}],\n  },\n  {\n    desc: md`The third rewrite uses the definition of <span class='leanmark'>\\`fib_odd_sum\\`</span> in reverse (hence the left arrow symbol \\`←\\`), which results in:\n\n${leanHL`⊢ fib_odd_sum n + fibonacci (2 * n + 1) = fibonacci (2 * (n + 1))`}`,\n    marks: [{from: {line:5, ch:2}, to: {line:5, ch:14}}],\n  },\n  {\n    desc: md`There are <span class='leanmark'>a few more rewrites</span> included in the Lean code which direct Lean to do some arithmetic. At the end though (try putting your cursor at the end of the highlighted region, right before the closing square bracket \"\\`]\\`\"), we have something that Lean recognizes is trivial and so it declares success with an uplifting \"\\`no goals\\`\" message in place of the tactic state!`,\n    marks: [{from: {line:6, ch:2}, to: {line:9, ch:11}}],\n  }\n]","pinned":false,"mode":"js","data":null,"name":null},{"id":1813,"value":"md`The editable (non-read-only) editors required a little bit of planning to set up. I wanted them to have access to definitions from the \\`fib_formulas.lean\\` editors without having to display them again. I also wanted their content to be independent from those editors, since if they shared a \\`docArray\\`, user input in one editor could screw up the parsing of later editors. \n\nSince the Lean server does not allow us to import from one open Lean file to another, I had to either copy all of the definitions that I wished to reuse into the \\`docArray\\` of every editable editor, or import from a file in the ZIP bundle. As I had already decided to make a custom ZIP bundle, I went for the latter.\n\nThe editable editor cells thus use arrays consisting of 2 strings. The first string contains \\`import fib\\`, so that all of my definitions are available, and then I also include an \\`open\\` statement for convenience. The second string contains the Lean code that the user has access to and can edit. I use \\`docIndex: 1\\` in the options so that only the second string is displayed. For instance, here's the source code for [the first editable editor](#fEval):\n\n\\`\\`\\`js\nviewof fEval = leanEditor({\n  docArray: [    \n    \\`import fib\nopen nat list\\`,\n    \\`#eval fibonacci 5\n#eval (range 10).map fibonacci\\`\n  ],\n  docIndex: 1,\n  fileName: 'fib_formula_eval.lean',\n  displayMode: 1,\n  lineNumbers: false,\n  height: '200px',\n  header: html\\`\\`,\n}, invalidation)\n\\`\\`\\`\n\nThe \\`header\\` option is used just to hide the default filename display. Note that the value of this cell \\`fEval\\` is used in the last cell before [this section](#sectionFormulas).`","pinned":false,"mode":"js","data":null,"name":null},{"id":2123,"value":"md`## Version history:\n- [2019/07/28](https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums@2106): first publish\n- [2019/08/15](https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums@2128): Compatibility with [2019/08/15 update to \"Hello, Lean prover!\"](https://observablehq.com/@bryangingechen/hello-lean-prover@5203).\n- [2020/03/06](https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums@2130): update for Lean 3.6.1c\n- [2020/04/19](https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums@2133): update for Lean 3.9.0c\n- [2020/05/22](https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums@2135): update for Lean 3.14.0c\n- 2021/01/28: update for Lean 3.26.0c\n`","pinned":false,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}