├── .gitignore ├── .replit ├── .watchmanconfig ├── README.md ├── agda ├── .gitignore └── hello.agda ├── coq └── division.v ├── hs ├── .gitignore ├── ChangeLog.md ├── README.md ├── Setup.hs ├── app │ └── Main.hs ├── hs.cabal ├── package.yaml ├── src │ └── Lib.hs ├── stack.yaml ├── stack.yaml.lock └── test │ └── Spec.hs ├── imo ├── .gitignore ├── leanpkg.toml └── src │ ├── imo1969-q1.lean │ └── testing.lean ├── isabelle ├── .gitignore ├── Division.thy └── DivisionOld.thy ├── js ├── almostPalindrome.js ├── almostPalindrome.test.js ├── anagrammedIndexOf.js ├── anagrammedIndexOf.test.js ├── arithmetic.js ├── arithmetic.test.js ├── arrayCount.js ├── arrayCount.test.js ├── balanceParens.js ├── balanceParens.test.js ├── choice.js ├── choice.test.js ├── coinSum.js ├── coinSum.test.js ├── combineDigits.js ├── combineDigits.test.js ├── countIslands.js ├── countIslands.test.js ├── depthSum.js ├── depthSum.test.js ├── dll.js ├── dll.test.js ├── findDominos.js ├── findDominos.test.js ├── findFamous.js ├── findFamous.test.js ├── findNodeClone.js ├── findNodeClone.test.js ├── flatten.js ├── flatten.test.js ├── football.js ├── football.test.js ├── inPlaceShift.js ├── inPlaceShift.test.js ├── inorder.js ├── isMirror.js ├── isMirror.test.js ├── isValidNumber.js ├── isValidNumber.test.js ├── lastable.js ├── lastable.test.js ├── lowestCommonAncestor.js ├── lowestCommonAncestor.test.js ├── makeTreeFromOrders.js ├── makeTreeFromOrders.test.js ├── maxConnectedSum.js ├── maxConnectedSum.test.js ├── mirrorTree.js ├── numInterpret.js ├── numInterpret.test.js ├── preorder.js ├── preorder.test.js ├── reverseList.js ├── reverseList.test.js ├── sampler.js ├── sampler.test.js ├── sequenceSum.js ├── sequenceSum.test.js ├── sqrt.js ├── sqrt.test.js ├── subgridCounter.js ├── subgridCounter.test.js ├── threeSum.js ├── threeSum.test.js ├── topWords.js ├── topWords.test.js ├── treeEqual.js ├── treeIsSorted.js ├── treeIsSorted.test.js ├── treeIterator.js ├── treeIterator.test.js ├── treeLevelMatch.js ├── treeLevelMatch.test.js ├── treeLongestPath.js ├── treeLongestPath.test.js ├── treeMarkDistance.js ├── treeMarkDistance.test.js ├── treeToList.js ├── treeToList.test.js ├── volcano.js └── volcano.test.js ├── lean ├── .gitignore ├── leanpkg.toml └── src │ └── tutorial.lean ├── lean4 └── division.lean ├── ocaml └── hello.ml ├── package.json ├── python ├── algebra.py ├── almost_palindrome.py ├── chess1d.py ├── drill.py ├── gen_problems.py ├── hello.py ├── lisp.py ├── mastermind.py ├── solver.py ├── substring_anagrams.py └── unification.py ├── replit.nix └── rust ├── .gitignore ├── almost_palindrome.rs ├── guessing_game ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs └── hello.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | npm-debug.log 3 | node_modules 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | run = "echo hello world" 2 | 3 | [nix] 4 | channel = "stable-22_11" 5 | 6 | [deployment] 7 | run = ["sh", "-c", "echo hello world"] 8 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kata 2 | If you want to get better at something, repeating practice alone is not enough. You must practice with increased difficulty and challenge. 3 | -------------------------------------------------------------------------------- /agda/.gitignore: -------------------------------------------------------------------------------- 1 | *.agdai 2 | -------------------------------------------------------------------------------- /agda/hello.agda: -------------------------------------------------------------------------------- 1 | data Greeting : Set where 2 | hello : Greeting 3 | 4 | greet : Greeting 5 | greet = hello 6 | 7 | 8 | data Nat : Set where 9 | zero : Nat 10 | suc : Nat -> Nat 11 | 12 | 13 | add : Nat -> Nat -> Nat 14 | add zero y = y 15 | add (suc x) y = suc (add x y) 16 | 17 | 18 | infixl 6 _+_ 19 | 20 | _+_ : Nat -> Nat -> Nat 21 | x + y = add x y 22 | -------------------------------------------------------------------------------- /coq/division.v: -------------------------------------------------------------------------------- 1 | Inductive cnat : Set := 2 | | Zero : cnat 3 | | Suc : cnat -> cnat. 4 | 5 | Fixpoint add (a : cnat) (b : cnat) : cnat := 6 | match a with 7 | | Zero => b 8 | | (Suc c) => Suc (add c b) 9 | end. 10 | 11 | Theorem add_zero_right : forall (x : cnat), add x Zero = x. 12 | Proof. 13 | intros x. 14 | induction x. 15 | simpl. 16 | trivial. 17 | simpl. 18 | rewrite IHx. 19 | trivial. 20 | Qed. 21 | 22 | Theorem add_suc_right : forall (x : cnat) (y : cnat), add x (Suc y) = Suc (add x y). 23 | Proof. 24 | intros x y. 25 | induction x. 26 | simpl. 27 | trivial. 28 | simpl. 29 | rewrite IHx. 30 | trivial. 31 | Qed. 32 | 33 | Theorem add_comm : forall (x : cnat) (y : cnat), add x y = add y x. 34 | Proof. 35 | intros x y. 36 | induction x. 37 | simpl. 38 | rewrite add_zero_right. 39 | trivial. 40 | simpl. 41 | rewrite add_suc_right. 42 | rewrite IHx. 43 | trivial. 44 | Qed. 45 | 46 | Theorem add_assoc : forall (x : cnat) (y : cnat) (z : cnat), add (add x y) z = add x (add y z). 47 | Proof. 48 | intros x y z. 49 | induction x. 50 | simpl. 51 | trivial. 52 | simpl. 53 | rewrite IHx. 54 | trivial. 55 | Qed. 56 | 57 | Fixpoint mul (a : cnat) (b : cnat) : cnat := 58 | match a with 59 | | Zero => Zero 60 | | (Suc c) => add b (mul c b) 61 | end. 62 | 63 | Theorem mul_zero_right : forall (x : cnat), mul x Zero = Zero. 64 | intros x. 65 | induction x. 66 | simpl. 67 | trivial. 68 | simpl. 69 | rewrite IHx. 70 | trivial. 71 | Qed. 72 | 73 | Theorem mul_suc_right : forall (x : cnat) (y : cnat), mul x (Suc y) = add x (mul x y). 74 | intros x y. 75 | induction x. 76 | simpl. 77 | trivial. 78 | simpl. 79 | rewrite IHx. 80 | rewrite add_comm. 81 | rewrite add_assoc. 82 | replace (add (mul x y) y) with (add y (mul x y)). 83 | trivial. 84 | rewrite add_comm. 85 | trivial. 86 | Qed. 87 | 88 | Theorem mul_comm : forall (x : cnat) (y : cnat), mul x y = mul y x. 89 | intros x y. 90 | induction x. 91 | simpl. 92 | rewrite mul_zero_right. 93 | trivial. 94 | simpl. 95 | rewrite mul_suc_right. 96 | rewrite IHx. 97 | trivial. 98 | Qed. 99 | 100 | Theorem distrib : forall (x : cnat) (y : cnat) (z : cnat), 101 | mul x (add y z) = add (mul x y) (mul x z). 102 | intros x y z. 103 | induction x. 104 | simpl. 105 | trivial. 106 | simpl. 107 | rewrite IHx. 108 | rewrite add_assoc. 109 | rewrite add_assoc. 110 | replace (add z (add (mul x y) (mul x z))) with (add (mul x y) (add z (mul x z))). 111 | trivial. 112 | rewrite <- add_assoc. 113 | rewrite <- add_assoc. 114 | replace (add (mul x y) z) with (add z (mul x y)). 115 | trivial. 116 | rewrite add_comm. 117 | trivial. 118 | Qed. 119 | 120 | Theorem mul_assoc : forall (x : cnat) (y : cnat) (z : cnat), 121 | mul (mul x y) z = mul x (mul y z). 122 | intros x y z. 123 | induction x. 124 | simpl. 125 | trivial. 126 | simpl. 127 | rewrite mul_comm. 128 | rewrite distrib. 129 | rewrite <- IHx. 130 | rewrite mul_comm. 131 | replace (mul z (mul x y)) with (mul (mul x y) z). 132 | trivial. 133 | rewrite mul_comm. 134 | trivial. 135 | Qed. 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /hs/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | *~ -------------------------------------------------------------------------------- /hs/ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for hs 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /hs/README.md: -------------------------------------------------------------------------------- 1 | # hs 2 | -------------------------------------------------------------------------------- /hs/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /hs/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main :: IO () 6 | main = someFunc 7 | -------------------------------------------------------------------------------- /hs/hs.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.33.0. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | -- 7 | -- hash: 7b4383e147014a136b0227a20898831be937fbaf9107c8076ea3be6a14742216 8 | 9 | name: hs 10 | version: 0.1.0.0 11 | description: Please see the README on GitHub at 12 | homepage: https://github.com/githubuser/hs#readme 13 | bug-reports: https://github.com/githubuser/hs/issues 14 | author: Author name here 15 | maintainer: example@example.com 16 | copyright: 2020 Author name here 17 | license: BSD3 18 | build-type: Simple 19 | extra-source-files: 20 | README.md 21 | ChangeLog.md 22 | 23 | source-repository head 24 | type: git 25 | location: https://github.com/githubuser/hs 26 | 27 | library 28 | exposed-modules: 29 | Lib 30 | other-modules: 31 | Paths_hs 32 | hs-source-dirs: 33 | src 34 | build-depends: 35 | base >=4.7 && <5 36 | default-language: Haskell2010 37 | 38 | executable hs-exe 39 | main-is: Main.hs 40 | other-modules: 41 | Paths_hs 42 | hs-source-dirs: 43 | app 44 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 45 | build-depends: 46 | base >=4.7 && <5 47 | , hs 48 | default-language: Haskell2010 49 | 50 | test-suite hs-test 51 | type: exitcode-stdio-1.0 52 | main-is: Spec.hs 53 | other-modules: 54 | Paths_hs 55 | hs-source-dirs: 56 | test 57 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 58 | build-depends: 59 | base >=4.7 && <5 60 | , hs 61 | default-language: Haskell2010 62 | -------------------------------------------------------------------------------- /hs/package.yaml: -------------------------------------------------------------------------------- 1 | name: hs 2 | version: 0.1.0.0 3 | github: "githubuser/hs" 4 | license: BSD3 5 | author: "Author name here" 6 | maintainer: "example@example.com" 7 | copyright: "2020 Author name here" 8 | 9 | extra-source-files: 10 | - README.md 11 | - ChangeLog.md 12 | 13 | # Metadata used when publishing your package 14 | # synopsis: Short description of your package 15 | # category: Web 16 | 17 | # To avoid duplicated efforts in documentation and dealing with the 18 | # complications of embedding Haddock markup inside cabal files, it is 19 | # common to point users to the README.md file. 20 | description: Please see the README on GitHub at 21 | 22 | dependencies: 23 | - base >= 4.7 && < 5 24 | 25 | library: 26 | source-dirs: src 27 | 28 | executables: 29 | hs-exe: 30 | main: Main.hs 31 | source-dirs: app 32 | ghc-options: 33 | - -threaded 34 | - -rtsopts 35 | - -with-rtsopts=-N 36 | dependencies: 37 | - hs 38 | 39 | tests: 40 | hs-test: 41 | main: Spec.hs 42 | source-dirs: test 43 | ghc-options: 44 | - -threaded 45 | - -rtsopts 46 | - -with-rtsopts=-N 47 | dependencies: 48 | - hs 49 | -------------------------------------------------------------------------------- /hs/src/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib 2 | ( foo 3 | ) where 4 | 5 | helper :: Int -> String 6 | 7 | helper n 8 | | n `mod` 15 == 0 = "FizzBuzz" 9 | | n `mod` 5 == 0 = "Buzz" 10 | | n `mod` 3 == 0 = "Fizz" 11 | | otherwise = show n 12 | 13 | 14 | 15 | foo :: IO () 16 | foo = sequence_ (map (putStrLn . helper) [1..20]) 17 | -------------------------------------------------------------------------------- /hs/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: lts-16.16 21 | 22 | # User packages to be built. 23 | # Various formats can be used as shown in the example below. 24 | # 25 | # packages: 26 | # - some-directory 27 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 28 | # subdirs: 29 | # - auto-update 30 | # - wai 31 | packages: 32 | - . 33 | # Dependency packages to be pulled from upstream that are not in the resolver. 34 | # These entries can reference officially published versions as well as 35 | # forks / in-progress versions pinned to a git hash. For example: 36 | # 37 | # extra-deps: 38 | # - acme-missiles-0.3 39 | # - git: https://github.com/commercialhaskell/stack.git 40 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 41 | # 42 | # extra-deps: [] 43 | 44 | # Override default flag values for local packages and extra-deps 45 | # flags: {} 46 | 47 | # Extra package databases containing global packages 48 | # extra-package-dbs: [] 49 | 50 | # Control whether we use the GHC we find on the path 51 | # system-ghc: true 52 | # 53 | # Require a specific version of stack, using version ranges 54 | # require-stack-version: -any # Default 55 | # require-stack-version: ">=2.3" 56 | # 57 | # Override the architecture used by stack, especially useful on Windows 58 | # arch: i386 59 | # arch: x86_64 60 | # 61 | # Extra directories used by stack for building 62 | # extra-include-dirs: [/path/to/dir] 63 | # extra-lib-dirs: [/path/to/dir] 64 | # 65 | # Allow a newer minor version of GHC than the snapshot specifies 66 | # compiler-check: newer-minor 67 | -------------------------------------------------------------------------------- /hs/stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | size: 532380 10 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/16.yaml 11 | sha256: d6b004b095fe2a0b8b14fbc30014ee97e58843b9c9362ddb9244273dda62649e 12 | original: lts-16.16 13 | -------------------------------------------------------------------------------- /hs/test/Spec.hs: -------------------------------------------------------------------------------- 1 | main :: IO () 2 | main = putStrLn "Test suite not yet implemented" 3 | -------------------------------------------------------------------------------- /imo/.gitignore: -------------------------------------------------------------------------------- 1 | *.olean 2 | /_target 3 | /leanpkg.path 4 | -------------------------------------------------------------------------------- /imo/leanpkg.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "imo" 3 | version = "0.1" 4 | lean_version = "leanprover-community/lean:3.20.0" 5 | path = "src" 6 | 7 | [dependencies] 8 | mathlib = {git = "https://github.com/leanprover-community/mathlib", rev = "3a591e8b693886dd48106faadbeac1136e92cd77"} 9 | -------------------------------------------------------------------------------- /imo/src/imo1969-q1.lean: -------------------------------------------------------------------------------- 1 | import tactic 2 | import tactic.basic 3 | import tactic.linarith 4 | import tactic.norm_cast 5 | import tactic.ring 6 | 7 | open int 8 | open nat 9 | 10 | /- 11 | The 1969 IMO, problem 1: 12 | Prove that there are infinitely many natural numbers $a$ with the following property: 13 | the number $z = n^4 + a$ is not prime for any natural number $n$. 14 | 15 | The key to the solution is that you can factor this into the product of two polynomials, if a = 4*m^4. 16 | -/ 17 | 18 | lemma factorization (m n: ℤ): (n^2 + 2*m^2 - 2*n*m) * (n^2 + 2*m^2 + 2*n*m) = n^4 + 4*m^4 19 | := by ring 20 | 21 | /- 22 | To show that the product is not prime, we need to show each of the factors is at least 2, which we can 23 | do with a sum-of-squares expression. 24 | -/ 25 | lemma left_factor_large (m n: ℤ) (h: m > 1): (n^2 + 2*m^2 - 2*n*m) > 1 := 26 | have h: (n^2 + 2*m^2 - 2*n*m) = (m-n)^2 + m^2, by ring, 27 | begin 28 | rw h, 29 | nlinarith, 30 | end 31 | 32 | lemma right_factor_large (m n: ℤ) (h: m > 1): (n^2 + 2*m^2 + 2*n*m) > 1 := 33 | have h: (n^2 + 2*m^2 + 2*n*m) = (m+n)^2 + m^2, by ring, 34 | begin 35 | rw h, 36 | nlinarith, 37 | end 38 | 39 | /- 40 | The factorization is over the integers, but we need the nonprimality over the natural numbers. 41 | -/ 42 | 43 | lemma int_large (a: ℤ) (h: a > 1) : a.nat_abs > 1 := 44 | have a = ↑(a.nat_abs) ∨ a = -↑(a.nat_abs), from nat_abs_eq a, 45 | or.elim this 46 | (assume: a = ↑(a.nat_abs), by linarith) 47 | (assume: a = -↑(a.nat_abs), by linarith) 48 | 49 | lemma int_not_prime (a b: ℤ) (c: ℕ) (h1: a > 1) (h2: b > 1) (h3: a*b = ↑c) : ¬ prime c := 50 | have h4: (a*b).nat_abs = a.nat_abs * b.nat_abs, from nat_abs_mul a b, 51 | have h5: a.nat_abs * b.nat_abs = c, by finish, 52 | norm_num.not_prime_helper a.nat_abs b.nat_abs c h5 (int_large a h1) (int_large b h2) 53 | 54 | lemma polynomial_not_prime (m n: ℕ) (h1: m > 1) : ¬ prime (n^4 + 4*m^4) := 55 | have h2: of_nat m > 1, from coe_nat_lt.mpr h1, 56 | begin 57 | refine int_not_prime _ _ _ (left_factor_large ↑m ↑n h2) (right_factor_large ↑m ↑n h2) _, 58 | rw factorization, 59 | norm_cast 60 | end 61 | 62 | 63 | /- 64 | Now we just need to show this works for an arbitrarily large a, to prove there are infinitely many of them. 65 | a = 4*(2+b)^4 should do. So m = 2+b. 66 | -/ 67 | 68 | theorem imo1969_q1: ∀ b: ℕ, ∃ a: ℕ, a ≥ b ∧ ∀ n: ℕ, ¬ prime (n^4 + a) := 69 | assume b, 70 | have h1: 2+b > 1, by linarith, 71 | have b^2 ≥ b, by nlinarith, 72 | have h2: 4*(2+b)^4 ≥ b, by nlinarith, 73 | begin 74 | fapply exists.intro, 75 | exact 4*(2+b)^4, 76 | apply and.intro h2, 77 | assume n, 78 | exact polynomial_not_prime (2+b) n h1 79 | end 80 | 81 | 82 | -------------------------------------------------------------------------------- /imo/src/testing.lean: -------------------------------------------------------------------------------- 1 | import tactic 2 | 3 | -------------------------------------------------------------------------------- /isabelle/.gitignore: -------------------------------------------------------------------------------- 1 | *# 2 | -------------------------------------------------------------------------------- /isabelle/Division.thy: -------------------------------------------------------------------------------- 1 | theory Division 2 | imports Main 3 | begin 4 | (* Let's get to the Division Theorem in the "structured" style. *) 5 | 6 | datatype cnat = Zero | Suc cnat 7 | 8 | fun add :: "cnat \ cnat \ cnat" where 9 | "add Zero x = x" | 10 | "add (Suc x) y = Suc (add x y)" 11 | 12 | lemma add_zero_right [simp]: "add x Zero = x" 13 | proof (induct x) 14 | case Zero show ?case by simp 15 | next 16 | case (Suc x) then show ?case by simp 17 | qed 18 | 19 | lemma add_suc_right [simp]: "add x (Suc y) = Suc (add x y)" 20 | proof (induct x) 21 | case Zero 22 | then show ?case 23 | by simp 24 | next 25 | case (Suc x) 26 | then show ?case 27 | by simp 28 | qed 29 | 30 | lemma add_comm: "add x y = add y x" 31 | proof (induct x) 32 | case Zero 33 | then show ?case 34 | by simp 35 | next 36 | case (Suc x) 37 | then show ?case 38 | by simp 39 | qed 40 | 41 | lemma add_assoc: "add (add x y) z = add x (add y z)" 42 | proof (induct x) 43 | case Zero 44 | then show ?case 45 | by simp 46 | next 47 | case (Suc x) 48 | then show ?case 49 | by simp 50 | qed 51 | 52 | fun mul :: "cnat \ cnat \ cnat" where 53 | "mul Zero x = Zero" | 54 | "mul (Suc x) y = add y (mul x y)" 55 | 56 | lemma mul_zero_right [simp]: "mul x Zero = Zero" 57 | proof (induct x) 58 | case Zero 59 | then show ?case 60 | by simp 61 | next 62 | case (Suc x) 63 | then show ?case 64 | by simp 65 | qed 66 | 67 | lemma mul_suc_right [simp]: "mul x (Suc y) = add x (mul x y)" 68 | proof (induct x) 69 | case Zero 70 | then show ?case 71 | by simp 72 | next 73 | case (Suc x) 74 | then show ?case 75 | using add_assoc add_comm by fastforce 76 | qed 77 | 78 | lemma mul_comm: "mul x y = mul y x" 79 | proof (induct x) 80 | case Zero 81 | then show ?case 82 | by simp 83 | next 84 | case (Suc x) 85 | then show ?case 86 | by simp 87 | qed 88 | 89 | lemma distrib [simp]: "mul x (add y z) = add (mul x y) (mul x z)" 90 | proof (induct x) 91 | case Zero 92 | then show ?case 93 | by simp 94 | next 95 | case (Suc x) 96 | then show ?case 97 | by (metis add_assoc add_comm mul.simps(2)) 98 | qed 99 | 100 | lemma mul_assoc: "mul (mul x y) z = mul x (mul y z)" 101 | proof (induct x) 102 | case Zero 103 | then show ?case 104 | by simp 105 | next 106 | case (Suc x) 107 | then show ?case 108 | by (simp add: mul_comm) 109 | qed 110 | 111 | fun lt :: "cnat \ cnat \ bool" where 112 | "lt x Zero = False" | 113 | "lt Zero x = True" | 114 | "lt (Suc x) (Suc y) = lt x y" 115 | 116 | lemma lt_not_ref: "~ lt x x" 117 | proof (induct x) 118 | case Zero 119 | then show ?case 120 | by simp 121 | next 122 | case (Suc x) 123 | then show ?case 124 | by simp 125 | qed 126 | 127 | lemma lt_not_symm: "lt a b \ ~ lt b a" 128 | proof (induct a arbitrary: b) 129 | case Zero 130 | then show ?case 131 | by simp 132 | next 133 | case (Suc a) 134 | then show ?case 135 | by (metis lt.elims(2) lt.simps(3)) 136 | qed 137 | 138 | lemma lt_trans: "lt a b \ lt b c \ lt a c" 139 | proof (induct c arbitrary: a b) 140 | case Zero 141 | then show ?case 142 | by simp 143 | next 144 | case (Suc c) 145 | then show ?case 146 | by (metis cnat.distinct(1) cnat.inject lt.elims(1) lt.simps(3)) 147 | qed 148 | 149 | lemma lt_to_sub: "lt a b \ \ c. b = add a c" 150 | proof (induct a arbitrary: b) 151 | case Zero 152 | then show ?case 153 | by simp 154 | next 155 | case (Suc a) 156 | then show ?case 157 | by (metis add.elims add.simps(2) lt.simps(1) lt.simps(3)) 158 | qed 159 | 160 | lemma lt_add_suc: "lt a (add a (Suc b))" 161 | proof (induct a arbitrary: b) 162 | case Zero 163 | then show ?case 164 | by simp 165 | next 166 | case (Suc a) 167 | then show ?case 168 | by simp 169 | qed 170 | 171 | lemma add_cancels_left: "add a b = add a c \ b = c" 172 | proof (induct a arbitrary: b) 173 | case Zero 174 | then show ?case 175 | by simp 176 | next 177 | case (Suc a) 178 | then show ?case 179 | by simp 180 | qed 181 | 182 | lemma add_cancels_right: "add a c = add b c \ a = b" 183 | using add_cancels_left add_comm by presburger 184 | 185 | lemma lt_suc: "lt a b \ (Suc a) = b \ lt (Suc a) b" 186 | by (smt (verit, ccfv_threshold) add.elims add_comm add_zero_right cnat.inject lt.elims(1) lt_add_suc lt_not_ref lt_to_sub) 187 | 188 | lemma division_theorem: "lt Zero n \ \ q r. lt r n \ m = add (mul q n) r" 189 | proof (induct m) 190 | case Zero 191 | then show ?case 192 | by (metis add_zero_right mul.simps(1)) 193 | next 194 | case (Suc m) 195 | obtain q and r where "lt r n \ m = add (mul q n) r" 196 | using Suc.hyps Suc.prems by blast 197 | show ?case 198 | proof (cases "Suc r = n") 199 | case True 200 | then show ?thesis 201 | by (metis Suc.prems \lt r n \ m = add (mul q n) r\ add.simps(2) add_comm add_zero_right mul_comm mul_suc_right) 202 | next 203 | case False 204 | have "Suc m = add (mul q n) (Suc r)" 205 | by (simp add: \lt r n \ m = add (mul q n) r\) 206 | have "lt (Suc r) n" 207 | using False \lt r n \ m = add (mul q n) r\ lt_suc by blast 208 | then show ?thesis 209 | using \cnat.Suc m = add (mul q n) (cnat.Suc r)\ by blast 210 | qed 211 | 212 | qed 213 | 214 | 215 | 216 | 217 | 218 | 219 | end -------------------------------------------------------------------------------- /isabelle/DivisionOld.thy: -------------------------------------------------------------------------------- 1 | theory Division 2 | imports Main 3 | begin 4 | 5 | (* "custom naturals" *) 6 | datatype cnat = Zero | Suc cnat 7 | 8 | fun add :: "cnat \ cnat \ cnat" where 9 | "add Zero x = x" | 10 | "add (Suc x) y = Suc (add x y)" 11 | 12 | lemma add_zero_right [simp]: "add x Zero = x" 13 | apply(induction x) 14 | apply(auto) 15 | done 16 | 17 | lemma add_suc_right [simp]: "add x (Suc y) = Suc (add x y)" 18 | apply(induction x) 19 | apply(auto) 20 | done 21 | 22 | lemma add_comm: "add x y = add y x" 23 | apply(induction x) 24 | apply(auto) 25 | done 26 | 27 | lemma add_assoc: "add (add x y) z = add x (add y z)" 28 | apply(induction x) 29 | apply(auto) 30 | done 31 | 32 | fun mul :: "cnat \ cnat \ cnat" where 33 | "mul Zero x = Zero" | 34 | "mul (Suc x) y = add y (mul x y)" 35 | 36 | lemma mul_zero_right [simp]: "mul x Zero = Zero" 37 | apply(induction x) 38 | apply(auto) 39 | done 40 | 41 | lemma mul_suc_right [simp]: "mul x (Suc y) = add x (mul x y)" 42 | apply(induction x) 43 | apply(simp) 44 | using add_assoc add_comm by auto 45 | 46 | lemma mul_comm: "mul x y = mul y x" 47 | apply(induction x) 48 | apply(auto) 49 | done 50 | 51 | lemma distrib [simp]: "mul x (add y z) = add (mul x y) (mul x z)" 52 | apply(induction x) 53 | apply(auto) 54 | by (metis add_assoc add_comm) 55 | 56 | lemma mul_assoc: "mul (mul x y) z = mul x (mul y z)" 57 | apply(induction x) 58 | apply(auto) 59 | by (simp add: mul_comm) 60 | 61 | fun lt :: "cnat \ cnat \ bool" where 62 | "lt x Zero = False" | 63 | "lt Zero x = True" | 64 | "lt (Suc x) (Suc y) = lt x y" 65 | 66 | lemma lt_not_ref: "~ lt x x" 67 | apply(induction x) 68 | apply(auto) 69 | done 70 | 71 | lemma lt_not_symm: "lt a b \ ~ lt b a" 72 | apply(induction a arbitrary: b) 73 | apply(auto) 74 | by (metis lt.elims(2) lt.simps(3)) 75 | 76 | lemma lt_trans: "lt a b \ lt b c \ lt a c" 77 | apply(induction c arbitrary: a b) 78 | apply(auto) 79 | by (metis cnat.distinct(1) cnat.inject lt.elims(1) lt.simps(3)) 80 | 81 | lemma lt_to_sub: "lt a b \ \ c. b = add a c" 82 | apply(induction a arbitrary: b) 83 | apply(auto) 84 | by (metis cnat.distinct(2) cnat.inject lt.elims(2)) 85 | 86 | lemma lt_add_suc: "lt a (add a (Suc b))" 87 | apply(induction a arbitrary: b) 88 | apply(auto) 89 | done 90 | 91 | lemma add_cancels_left: "add a b = add a c \ b = c" 92 | apply(induction a arbitrary: b c) 93 | apply(auto) 94 | done 95 | 96 | lemma add_cancels_right: "add a c = add b c \ a = b" 97 | using add_cancels_left add_comm by presburger 98 | 99 | lemma division_theorem: "lt Zero n \ \ q r. lt r n \ m = add (mul q n) r" 100 | apply(induction m) 101 | apply(auto) 102 | apply(metis add_zero_right mul.simps(1)) 103 | apply(case_tac "Suc r = n") 104 | apply (metis add.simps(2) add_comm add_zero_right mul_comm mul_suc_right) 105 | apply(subgoal_tac "lt (Suc r) n \ cnat.Suc (add (mul q n) r) = add (mul q n) (Suc r)") 106 | apply blast 107 | by (smt (verit) add.elims add_comm cnat.inject lt.elims(1) lt_add_suc lt_not_ref lt_to_sub) 108 | 109 | definition divides :: "cnat \ cnat \ bool" where 110 | "divides a b \ (\ m. (mul a m) = b)" 111 | 112 | definition prime :: "cnat \ bool" where 113 | "prime p \ lt (Suc Zero) p \ (\ d. divides d p \ d = (Suc Zero) \ d = p)" 114 | 115 | end -------------------------------------------------------------------------------- /js/almostPalindrome.js: -------------------------------------------------------------------------------- 1 | // Checks if this string is a palindrome from index i to j, inclusive. 2 | function isPalindrome(s, i, j) { 3 | if (j <= i) { 4 | return true; 5 | } 6 | if (s[i] !== s[j]) { 7 | return false; 8 | } 9 | return isPalindrome(s, i + 1, j - 1); 10 | } 11 | 12 | // Checks if you can make the string s from i to j, inclusive, into a 13 | // palindrome by removing at most one character. 14 | function almostPalindromeHelper(s, i, j) { 15 | if (j <= i) { 16 | return true; 17 | } 18 | if (s[i] === s[j]) { 19 | return almostPalindromeHelper(s, i + 1, j - 1); 20 | } 21 | 22 | // If s[i] and s[j] are different, either s[i] has to be removed, or 23 | // s[j] has to be removed. 24 | return isPalindrome(s, i + 1, j) || isPalindrome(s, i, j - 1); 25 | } 26 | 27 | function almostPalindrome(s) { 28 | return almostPalindromeHelper(s, 0, s.length - 1); 29 | } 30 | 31 | module.exports = almostPalindrome; 32 | 33 | -------------------------------------------------------------------------------- /js/almostPalindrome.test.js: -------------------------------------------------------------------------------- 1 | let almostPalindrome = require('./almostPalindrome'); 2 | 3 | test('exact', () => { 4 | expect(almostPalindrome('arfbarfrabfra')).toBe(true); 5 | expect(almostPalindrome('10011001')).toBe(true); 6 | }); 7 | 8 | test('off by one', () => { 9 | expect(almostPalindrome('arfbarfrabbfra')).toBe(true); 10 | expect(almostPalindrome('100211001')).toBe(true); 11 | }); 12 | 13 | test('way off', () => { 14 | expect(almostPalindrome('arfbarfarfbarf')).toBe(false); 15 | expect(almostPalindrome('10010110')).toBe(false); 16 | }); 17 | -------------------------------------------------------------------------------- /js/anagrammedIndexOf.js: -------------------------------------------------------------------------------- 1 | /* 2 | anagrammed indexOf 3 | ("actor, "cat") = 0 // 'act' is an anagram of 'cat' 4 | 5 | Note: this is only intended to work on [a-z] 6 | */ 7 | 8 | let charmap = new Map(); 9 | 10 | for (let char of 'abcdefghijklmnopqrstuvwxyz') { 11 | // 32 bits 12 | let n = Math.floor(Math.random() * 4294967296); 13 | charmap.set(char, n); 14 | } 15 | 16 | // Could fail but should work. 17 | function anagrammedIndexOf(bigWord, littleWord) { 18 | let len = littleWord.length; 19 | let hash = 0; 20 | for (let ch of littleWord) { 21 | if (!charmap.has(ch)) { 22 | throw new Error('bad char: ' + ch); 23 | } 24 | hash = hash ^ charmap.get(ch); 25 | } 26 | for (let i = 0; i < bigWord.length; i++) { 27 | // See if bigWord[i - len + 1] .. bigWord[i] makes the word 28 | let newChar = bigWord[i]; 29 | if (!charmap.has(newChar)) { 30 | throw new Error('weird char: ' + newChar); 31 | } 32 | hash = hash ^ charmap.get(newChar); 33 | let drop = i - len; 34 | if (drop >= 0) { 35 | hash = hash ^ charmap.get(bigWord[drop]); 36 | } 37 | if (drop >= -1 && hash === 0) { 38 | return drop + 1; 39 | } 40 | } 41 | return -1; 42 | } 43 | 44 | module.exports = anagrammedIndexOf; 45 | -------------------------------------------------------------------------------- /js/anagrammedIndexOf.test.js: -------------------------------------------------------------------------------- 1 | let aio = require('./anagrammedIndexOf'); 2 | 3 | test('basic example', () => { 4 | expect(aio('actor', 'cat')).toBe(0); 5 | expect(aio('actor', 'rot')).toBe(2); 6 | expect(aio('actor', 'cot')).toBe(1); 7 | expect(aio('actor', 'foo')).toBe(-1); 8 | }); 9 | -------------------------------------------------------------------------------- /js/arithmetic.js: -------------------------------------------------------------------------------- 1 | // Evaluates an arithmetic expression. 2 | // Only handles + and * 3 | function evaluate(expression) { 4 | let parts = expression.split(/[+]/); 5 | let answer = 0; 6 | for (let part of parts) { 7 | let subparts = part.split(/[*]/); 8 | let subanswer = 1; 9 | for (let subpart of subparts) { 10 | subanswer *= subpart; 11 | } 12 | answer += subanswer; 13 | } 14 | return answer; 15 | } 16 | 17 | 18 | module.exports = evaluate; 19 | -------------------------------------------------------------------------------- /js/arithmetic.test.js: -------------------------------------------------------------------------------- 1 | let evaluate = require('./arithmetic'); 2 | 3 | test('basic formula', () => { 4 | let answer = evaluate('12*12+7*8'); 5 | expect(answer).toBe(200); 6 | }); 7 | -------------------------------------------------------------------------------- /js/arrayCount.js: -------------------------------------------------------------------------------- 1 | 2 | // Find the index of k in the array. array is sorted array of ints. 3 | // Only return it if it is in [i, j] 4 | // Return -1 if it isn't there 5 | function indexOf(array, i, j, k) { 6 | if (i > j) { 7 | return -1; 8 | } 9 | if (i === j) { 10 | if (array[i] === k) { 11 | return i; 12 | } else { 13 | return -1; 14 | } 15 | } 16 | let mid = Math.floor((i + j) / 2); 17 | let val = array[mid]; 18 | if (val === k) { 19 | return mid; 20 | } 21 | if (val > k) { 22 | return indexOf(array, i, mid - 1, k ); 23 | } else { 24 | return indexOf(array, mid + 1, j, k); 25 | } 26 | } 27 | 28 | // In a sorted array of integers, count how many times a particular one appears. 29 | function arrayCount(array, k) { 30 | let i = indexOf(array, 0, array.length - 1, k); 31 | if (i === -1) { 32 | return 0; 33 | } 34 | let min = i; 35 | while (min > 0 && array[min - 1] === k) { 36 | min -= 1; 37 | } 38 | let max = i; 39 | while (max < array.length - 1 && array[max + 1] === k) { 40 | max += 1; 41 | } 42 | return max - min + 1; 43 | } 44 | 45 | module.exports = arrayCount; 46 | -------------------------------------------------------------------------------- /js/arrayCount.test.js: -------------------------------------------------------------------------------- 1 | const arrayCount = require('./arrayCount'); 2 | 3 | test('normal behavior', () => { 4 | expect(arrayCount([1, 4, 7, 7, 7, 8, 9], 7)).toBe(3); 5 | }); 6 | 7 | test('nonexistent', () => { 8 | expect(arrayCount([3, 4, 5, 6, 8, 9, 10], 7)).toBe(0); 9 | }); 10 | 11 | test('empty', () => { 12 | expect(arrayCount([], 5)).toBe(0); 13 | }); 14 | -------------------------------------------------------------------------------- /js/balanceParens.js: -------------------------------------------------------------------------------- 1 | // Returns a string with balanced parentheses while removing 2 | // as few characters as possible from str. 3 | function balanceParens(str) { 4 | // Counts opens - closed. 5 | let opens = 0; 6 | 7 | // First pass: Remove extra close-parens 8 | let firstPass = []; 9 | for (let char of str) { 10 | if (char === '(') { 11 | opens += 1; 12 | firstPass.push('('); 13 | } else if (char === ')') { 14 | if (opens === 0) { 15 | // We have to remove this character 16 | } else { 17 | opens -= 1; 18 | firstPass.push(')'); 19 | } 20 | } else { 21 | firstPass.push(char); 22 | } 23 | } 24 | 25 | // Second pass: remove extra open-parens 26 | let output = []; 27 | for (let char of firstPass) { 28 | if (opens > 0 && char === '(') { 29 | // Remove this one 30 | opens -= 1; 31 | } else { 32 | output.push(char); 33 | } 34 | } 35 | 36 | return output.join(''); 37 | } 38 | 39 | module.exports = balanceParens; 40 | -------------------------------------------------------------------------------- /js/balanceParens.test.js: -------------------------------------------------------------------------------- 1 | const balanceParens = require('./balanceParens'); 2 | 3 | test('already balanced', () => { 4 | let x = 'arf(bar(zz()(a(v(f)g)h))h)(hh())' 5 | expect(balanceParens(x)).toBe(x); 6 | }); 7 | 8 | test('complex example', () => { 9 | expect(balanceParens(')((())')).toBe('(())'); 10 | }); 11 | -------------------------------------------------------------------------------- /js/choice.js: -------------------------------------------------------------------------------- 1 | // Pick k items from the list randomly 2 | function choice(list, numToChoose) { 3 | let total = list.length; 4 | if (total < numToChoose) { 5 | throw new Error( 6 | 'cannot choose ' + numToChoose + 7 | ' from a list of length ' + total); 8 | } 9 | 10 | let output = []; 11 | while (numToChoose > 0) { 12 | // Consider taking index total - 1 13 | // There is a numToChoose / total chance of wanting it 14 | let chance = numToChoose / total; 15 | if (Math.random() < chance) { 16 | // Do select it 17 | output.push(list[total - 1]); 18 | numToChoose -= 1; 19 | } 20 | total -= 1; 21 | } 22 | return output; 23 | } 24 | 25 | module.exports = choice; 26 | -------------------------------------------------------------------------------- /js/choice.test.js: -------------------------------------------------------------------------------- 1 | const choice = require('./choice'); 2 | 3 | test('right length', () => { 4 | expect(choice([1, 2, 3, 4], 0).length).toBe(0); 5 | expect(choice([1, 2, 3, 4], 1).length).toBe(1); 6 | expect(choice([1, 2, 3, 4], 2).length).toBe(2); 7 | expect(choice([1, 2, 3, 4], 3).length).toBe(3); 8 | expect(choice([1, 2, 3, 4], 4).length).toBe(4); 9 | }); 10 | 11 | test('edge cases', () => { 12 | expect(() => choice([1, 2], 3)).toThrow(); 13 | expect(choice([], 0)).toEqual([]); 14 | expect(choice([3, 3, 3], 3)).toEqual([3, 3, 3]); 15 | }); 16 | -------------------------------------------------------------------------------- /js/coinSum.js: -------------------------------------------------------------------------------- 1 | // This class can be used for a single op, because it memoizes. 2 | class CoinSummer { 3 | constructor(coins) { 4 | // The keys are comma-separated answers to count: 5 | // numCoins,target 6 | this.memo = new Map(); 7 | 8 | this.coins = coins; 9 | } 10 | 11 | // How many combinations of the first numCoins coin types sum to the 12 | // target value. 13 | count(numCoins, target) { 14 | if (numCoins === 0) { 15 | return target === 0 ? 1 : 0; 16 | } 17 | 18 | let key = numCoins + ',' + target; 19 | if (this.memo.has(key)) { 20 | return this.memo.get(key); 21 | } 22 | 23 | let lastCoin = this.coins[numCoins - 1]; 24 | 25 | // One possibility is to not use lastCoin at all 26 | let answer = this.count(numCoins - 1, target); 27 | 28 | // Another possibility is to use lastCoin at least once 29 | if (lastCoin <= target) { 30 | answer += this.count(numCoins, target - lastCoin); 31 | } 32 | 33 | this.memo.set(key, answer); 34 | return answer; 35 | } 36 | } 37 | 38 | // Given a sorted list of denominations of coins and a target value, 39 | // determine how many combinations of the coin types sum to the target 40 | // value. 41 | function coinSum(coins, target) { 42 | let summer = new CoinSummer(coins); 43 | return summer.count(coins.length, target); 44 | } 45 | 46 | module.exports = coinSum; 47 | -------------------------------------------------------------------------------- /js/coinSum.test.js: -------------------------------------------------------------------------------- 1 | const coinSum = require('./coinSum'); 2 | 3 | test('regular american coins', () => { 4 | expect(coinSum([1, 5, 10, 25], 13)).toBe(4); 5 | }); 6 | -------------------------------------------------------------------------------- /js/combineDigits.js: -------------------------------------------------------------------------------- 1 | // Returns a list of all the ways of combining + and - with the given string 2 | function allWays(str) { 3 | if (str.length <= 0) { 4 | return ['']; 5 | } 6 | let subanswer = allWays(str.slice(1)); 7 | let answer = []; 8 | for (let way of subanswer) { 9 | answer.push(str[0] + way); 10 | answer.push('-' + str[0] + way); 11 | answer.push('+' + str[0] + way); 12 | } 13 | return answer; 14 | } 15 | 16 | // Returns the number of ways of combining + and - with 123456789 that 17 | // evals to 100 18 | // Can't put a plus before the first one 19 | function combineDigits() { 20 | let answer = 0; 21 | let ways = allWays('123456789'); 22 | for (let way of ways) { 23 | if (way[0] === '+') { 24 | continue; 25 | } 26 | if (eval(way) === 100) { 27 | answer += 1; 28 | } 29 | } 30 | return answer; 31 | } 32 | 33 | module.exports = combineDigits; 34 | -------------------------------------------------------------------------------- /js/combineDigits.test.js: -------------------------------------------------------------------------------- 1 | let combineDigits = require('./combineDigits'); 2 | 3 | test('combining digits runs', () => { 4 | expect(combineDigits()).toBeGreaterThan(0); 5 | }); 6 | -------------------------------------------------------------------------------- /js/countIslands.js: -------------------------------------------------------------------------------- 1 | // Replaces [i][j] from 1 -> 0 and recurses locally 2 | // Returns whether there was a replace 3 | function flood(matrix, i, j) { 4 | if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[i].length) { 5 | return false; 6 | } 7 | if (matrix[i][j] === 0) { 8 | return false; 9 | } 10 | matrix[i][j] = 0; 11 | flood(matrix, i - 1, j); 12 | flood(matrix, i + 1, j); 13 | flood(matrix, i, j - 1); 14 | flood(matrix, i, j + 1); 15 | return true; 16 | } 17 | 18 | function countIslands(matrix) { 19 | let copy = JSON.parse(JSON.stringify(matrix)); 20 | let answer = 0; 21 | for (let i = 0; i < copy.length; i++) { 22 | for (let j = 0; j < copy[i].length; j++) { 23 | if (flood(copy, i, j)) { 24 | answer++; 25 | } 26 | } 27 | } 28 | return answer; 29 | } 30 | 31 | module.exports = countIslands; 32 | -------------------------------------------------------------------------------- /js/countIslands.test.js: -------------------------------------------------------------------------------- 1 | const countIslands = require('./countIslands'); 2 | 3 | test('two islands', () => { 4 | let matrix = [ 5 | [0, 0, 1], 6 | [1, 0, 1], 7 | [1, 0, 0], 8 | ]; 9 | expect(countIslands(matrix)).toBe(2); 10 | 11 | // Make sure it works twice 12 | expect(countIslands(matrix)).toBe(2); 13 | }); 14 | 15 | test('no islands', () => { 16 | let matrix = [ 17 | [0, 0], 18 | [0, 0], 19 | ]; 20 | expect(countIslands(matrix)).toBe(0); 21 | }); 22 | 23 | test('diagonals do not count', () => { 24 | let matrix = [ 25 | [1, 0], 26 | [0, 1], 27 | ]; 28 | expect(countIslands(matrix)).toBe(2); 29 | }); 30 | -------------------------------------------------------------------------------- /js/depthSum.js: -------------------------------------------------------------------------------- 1 | // Given an array of that contains both integers and nested arrays, 2 | // calculate and return the depth sum. 3 | // For example: 4 | // Input: [8, 4, [5, 3, [9]], 6] 5 | // Output: 8+4+2*(5+3)+3*9+6 ==> 61 6 | 7 | // Returns the depth sum, where 'depth' is the deepness we already are at. 8 | function helper(array, depth) { 9 | let answer = 0; 10 | for (let item of array) { 11 | if (Array.isArray(item)) { 12 | answer += helper(item, depth + 1); 13 | } else { 14 | answer += depth * item; 15 | } 16 | } 17 | return answer; 18 | } 19 | 20 | function depthSum(array) { 21 | return helper(array, 1); 22 | } 23 | 24 | module.exports = depthSum; 25 | -------------------------------------------------------------------------------- /js/depthSum.test.js: -------------------------------------------------------------------------------- 1 | const depthSum = require('./depthSum'); 2 | 3 | test('standard problem', () => { 4 | let input = [8, 4, [5, 3, [9]], 6]; 5 | expect(depthSum(input)).toBe(61); 6 | }); 7 | 8 | test('nothing', () => { 9 | let input = [[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]; 10 | expect(depthSum(input)).toBe(0); 11 | }) 12 | -------------------------------------------------------------------------------- /js/dll.js: -------------------------------------------------------------------------------- 1 | // A doubly-linked list class. 2 | // Each item has prev, next, and value. 3 | class DLL { 4 | constructor(value, prev, next) { 5 | this.value = value; 6 | this.prev = prev || null; 7 | this.next = next || null; 8 | } 9 | 10 | // Removes this node from a DLL 11 | // The prev and next will still be valid after removal. 12 | remove() { 13 | let prev = this.prev; 14 | let next = this.next; 15 | if (prev) { 16 | this.prev.next = next; 17 | } 18 | if (next) { 19 | this.next.prev = prev; 20 | } 21 | } 22 | 23 | // Call this on the first node in a list 24 | toArray() { 25 | let answer = []; 26 | let node = this; 27 | while (node) { 28 | answer.push(node.value); 29 | node = node.next; 30 | } 31 | return answer; 32 | } 33 | 34 | join(connector) { 35 | return this.toArray().join(connector); 36 | } 37 | 38 | // Returns a new DLL with this value inserted at the beginning 39 | // Can only be called on the head 40 | // Returns the new head 41 | prepend(value) { 42 | if (this.prev) { 43 | throw new Error('prepend should only be called on the head'); 44 | } 45 | let newNode = new DLL(value, null, this); 46 | this.prev = newNode; 47 | return newNode; 48 | } 49 | 50 | // Returns a new DLL with this value appended at the end 51 | // Can only be called on the tail 52 | // Returns the new tail 53 | append(value) { 54 | if (this.next) { 55 | throw new Error('append should only be called on the tail'); 56 | } 57 | let newNode = new DLL(value, this, null); 58 | this.next = newNode; 59 | return newNode; 60 | } 61 | } 62 | 63 | module.exports = DLL; 64 | -------------------------------------------------------------------------------- /js/dll.test.js: -------------------------------------------------------------------------------- 1 | let DLL = require('./dll'); 2 | 3 | test('basic DLL ops', () => { 4 | let node = new DLL(1); 5 | expect(node.prev).toBe(null); 6 | expect(node.next).toBe(null); 7 | }); 8 | 9 | test('remove', () => { 10 | let head = new DLL('A'); 11 | head.append('B').append('C').append('D'); 12 | expect(head.join(',')).toBe('A,B,C,D'); 13 | head.next.remove(); 14 | expect(head.join(',')).toBe('A,C,D'); 15 | }); 16 | 17 | test('prepend', () => { 18 | let tail = new DLL('Z'); 19 | let head = tail.prepend('Y').prepend('X'); 20 | expect(head.join(',')).toBe('X,Y,Z'); 21 | }); 22 | -------------------------------------------------------------------------------- /js/findDominos.js: -------------------------------------------------------------------------------- 1 | 2 | // Given a list of pairs (dominos) of integers, and an integer K, 3 | // find 2 dominos that their left values sum to K and their right values 4 | // sum to K. 5 | 6 | function getKey(left, right) { 7 | return left + ',' + right; 8 | } 9 | 10 | function getPairKey(k, left, right) { 11 | let pairLeft = k - left; 12 | let pairRight = k - right; 13 | return getKey(pairLeft, pairRight); 14 | } 15 | 16 | function findDominos(list, k) { 17 | let dominos = new Map(); 18 | for (let domino of list) { 19 | let [left, right] = domino; 20 | let key = getKey(left, right); 21 | let pairKey = getPairKey(k, left, right); 22 | if (dominos.has(pairKey)) { 23 | return [dominos.get(pairKey), domino]; 24 | } 25 | dominos.set(key, domino); 26 | } 27 | return -1; 28 | } 29 | 30 | module.exports = findDominos; 31 | -------------------------------------------------------------------------------- /js/findDominos.test.js: -------------------------------------------------------------------------------- 1 | const findDominos = require('./findDominos'); 2 | 3 | test('there is a match', () => { 4 | let data = [[1, 9], [2, 2], [3, 3], [7, 2], [9, 1], [-5, 15]]; 5 | expect(findDominos(data, 10)).toEqual([[1, 9], [9, 1]]); 6 | }); 7 | 8 | test('there is no match', () => { 9 | let data = [[1, 2], [3, 4], [5, 5]]; 10 | expect(findDominos(data, 10)).toEqual(-1); 11 | }); 12 | -------------------------------------------------------------------------------- /js/findFamous.js: -------------------------------------------------------------------------------- 1 | // Given a list of persons, find out the "famous person" in the list. 2 | // A person is a "famous person" iff all others knows him/her AND he/she 3 | // doesn't know anybody else. 4 | // Take a function bool know(i, j) which return true if 5 | // i knows j, or false if i does not know j. 6 | // The people are numbered 0 to n-1. 7 | // Return -1 if there is no famous person. 8 | function findFamous(know, n) { 9 | if (n <= 0) { 10 | return -1; 11 | } 12 | // Run a tournament to figure out who wins. 13 | // Loop invariant: winner is the only person who could be famous. 14 | let winner = 0; 15 | for (let challenger = 1; challenger < n; challenger++) { 16 | if (know(winner, challenger)) { 17 | // winner cannot be famous. challenger could be. 18 | winner = challenger; 19 | } else { 20 | // winner could be famous. challenger could not be. 21 | } 22 | } 23 | 24 | // Now check if winner actually is famous. 25 | for (let other = 0; other < n; other++) { 26 | if (other === winner) { 27 | continue; 28 | } 29 | if (know(winner, other) || !know(other, winner)) { 30 | // winner is not famous, so nobody can be 31 | return -1; 32 | } 33 | } 34 | return winner; 35 | } 36 | 37 | module.exports = findFamous; 38 | -------------------------------------------------------------------------------- /js/findFamous.test.js: -------------------------------------------------------------------------------- 1 | const findFamous = require('./findFamous'); 2 | 3 | function fiveIsFamous(i, j) { 4 | if (i === 5) { 5 | return false; 6 | } 7 | if (j === 5) { 8 | return true; 9 | } 10 | return (i % 3) === (j % 3) ? true : false; 11 | } 12 | 13 | test('finding five', () => { 14 | expect(findFamous(fiveIsFamous, 1000)).toBe(5); 15 | }); 16 | 17 | test('when there is no five', () => { 18 | expect(findFamous(fiveIsFamous, 3)).toBe(-1); 19 | }); 20 | -------------------------------------------------------------------------------- /js/findNodeClone.js: -------------------------------------------------------------------------------- 1 | // Add "parent" links to a tree. 2 | // For the root, parent should be null. 3 | function addParentLinks(tree, parent) { 4 | if (!tree) { 5 | return; 6 | } 7 | tree.parent = parent; 8 | addParentLinks(tree.left, tree); 9 | addParentLinks(tree.right, tree); 10 | } 11 | 12 | // Clones left, right, value, and parent 13 | function clone(tree) { 14 | if (!tree) { 15 | return null; 16 | } 17 | let leftClone = clone(tree.left); 18 | let rightClone = clone(tree.right); 19 | let answer = { 20 | parent: null, 21 | value: tree.value, 22 | left: leftClone, 23 | right: rightClone, 24 | }; 25 | if (leftClone) { 26 | leftClone.parent = answer; 27 | } 28 | if (rightClone) { 29 | rightClone.parent = answer; 30 | } 31 | return answer; 32 | } 33 | 34 | // Finds the clone of node in tree 35 | function findNodeClone(node, tree) { 36 | if (!node.parent) { 37 | return tree; 38 | } 39 | let parentClone = findNodeClone(node.parent, tree); 40 | if (node === node.parent.left) { 41 | return parentClone.left; 42 | } else { 43 | return parentClone.right; 44 | } 45 | } 46 | 47 | module.exports = { findNodeClone, clone, addParentLinks }; 48 | -------------------------------------------------------------------------------- /js/findNodeClone.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | findNodeClone, 3 | clone, 4 | addParentLinks, 5 | } = require('./findNodeClone'); 6 | 7 | function makeTree() { 8 | let tree = { 9 | value: 'A', 10 | left: { 11 | value: 'B', 12 | }, 13 | right: { 14 | value: 'C', 15 | } 16 | }; 17 | addParentLinks(tree); 18 | return tree; 19 | } 20 | 21 | test('tree-making', () => { 22 | let tree = makeTree(); 23 | expect(tree.left.value).toBe('B'); 24 | expect(tree.left.parent.value).toBe('A'); 25 | expect(tree.right.value).toBe('C'); 26 | expect(tree.right.parent).toBe(tree); 27 | }); 28 | 29 | test('cloning', () => { 30 | let tree1 = makeTree(); 31 | let tree2 = clone(tree1); 32 | expect(tree2.left.value).toBe('B'); 33 | expect(tree2.right.parent.value).toBe('A'); 34 | }); 35 | 36 | test('finding the clone', () => { 37 | let tree1 = makeTree(); 38 | let tree2 = clone(tree1); 39 | expect(findNodeClone(tree1.left, tree2)).toBe(tree2.left); 40 | }); 41 | -------------------------------------------------------------------------------- /js/flatten.js: -------------------------------------------------------------------------------- 1 | function flatten(array) { 2 | let answer = []; 3 | for (let item of array) { 4 | if (Array.isArray(item)) { 5 | for (let subitem of flatten(item)) { 6 | answer.push(subitem); 7 | } 8 | } else { 9 | answer.push(item); 10 | } 11 | } 12 | return answer; 13 | } 14 | 15 | module.exports = flatten; 16 | -------------------------------------------------------------------------------- /js/flatten.test.js: -------------------------------------------------------------------------------- 1 | const flatten = require('./flatten'); 2 | 3 | test('flattening a random thing', () => { 4 | let input = [[1, 2], [[[[[[3]]]]]], 4, 5, [6, [7, [8, [9]]]]]; 5 | expect(flatten(input)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); 6 | }); 7 | -------------------------------------------------------------------------------- /js/football.js: -------------------------------------------------------------------------------- 1 | // Returns the number of ways to get n out of 2, 3, and 7. 2 | // Let's say order matters. 3 | let MEMO = new Map(); 4 | 5 | function football(n) { 6 | if (MEMO.has(n)) { 7 | return MEMO.get(n); 8 | } 9 | if (n === 0) { 10 | return 1; 11 | } 12 | if (n < 2) { 13 | return 0; 14 | } 15 | let answer = football(n - 2) + football(n - 3) + football(n - 7); 16 | MEMO.set(n, answer); 17 | return answer; 18 | } 19 | 20 | module.exports = football; 21 | -------------------------------------------------------------------------------- /js/football.test.js: -------------------------------------------------------------------------------- 1 | const football = require('./football'); 2 | 3 | test('basic operation', () => { 4 | expect(football(6)).toBe(2); 5 | 6 | // 6 ways to get it from 7 3 2 7 | // All 3's 8 | // All 2's 9 | // 3 3 2 2 2 leads to 5 choose 2 = 10 10 | expect(football(12)).toBe(18); 11 | 12 | expect(football(-1)).toBe(0); 13 | }); 14 | -------------------------------------------------------------------------------- /js/inPlaceShift.js: -------------------------------------------------------------------------------- 1 | // Handles negative numbers appropriately 2 | function mod(n, modulus) { 3 | return ((n % modulus) + modulus) % modulus; 4 | } 5 | 6 | // Right circular k-shift, in place, starting at i. 7 | // So this shifts i, i + k, ... ending at i. 8 | // Returns how many items got shifted. 9 | function helper(array, k, i) { 10 | let count = 0; 11 | let index = i; 12 | let carry = array[index]; 13 | while (true) { 14 | index = mod(index + k, array.length); 15 | let temp = array[index]; 16 | array[index] = carry; 17 | carry = temp; 18 | count++; 19 | if (index === i) { 20 | break; 21 | } 22 | } 23 | return count; 24 | } 25 | 26 | // Right circular k-shift everything in the array 27 | function inPlaceShift(array, k) { 28 | if (k === 0) { 29 | return; 30 | } 31 | let todo = array.length; 32 | for (let i = 0; todo > 0; i++) { 33 | todo -= helper(array, k, i); 34 | } 35 | } 36 | 37 | module.exports = inPlaceShift; 38 | -------------------------------------------------------------------------------- /js/inPlaceShift.test.js: -------------------------------------------------------------------------------- 1 | const inPlaceShift = require('./inPlaceShift'); 2 | 3 | test('relatively prime', () => { 4 | let arr = [1, 2, 3, 4, 5, 6, 7]; 5 | inPlaceShift(arr, 3); 6 | expect(arr).toEqual([5, 6, 7, 1, 2, 3, 4]); 7 | }); 8 | 9 | test('both even', () => { 10 | let arr = [1, 2, 3, 4, 5, 6]; 11 | inPlaceShift(arr, 2); 12 | expect(arr).toEqual([5, 6, 1, 2, 3, 4]); 13 | }); 14 | 15 | test('zero', () => { 16 | let arr = [1, 2, 3]; 17 | inPlaceShift(arr, 0); 18 | expect(arr).toEqual([1, 2, 3]); 19 | }); 20 | 21 | test('negative numbers', () => { 22 | let arr = [1, 2, 3, 4, 5, 6, 7]; 23 | inPlaceShift(arr, -3); 24 | expect(arr).toEqual([4, 5, 6, 7, 1, 2, 3]); 25 | }); 26 | 27 | test('big negative numbers', () => { 28 | let arr = [1, 2]; 29 | inPlaceShift(arr, -100); 30 | expect(arr).toEqual([1, 2]); 31 | }); 32 | -------------------------------------------------------------------------------- /js/inorder.js: -------------------------------------------------------------------------------- 1 | // Appends the inorder onto output 2 | function inorderHelper(tree, output) { 3 | if (!tree) { 4 | return; 5 | } 6 | inorderHelper(tree.left, output); 7 | output.push(tree.value); 8 | inorderHelper(tree.right, output); 9 | } 10 | 11 | function inorder(tree) { 12 | let answer = []; 13 | inorderHelper(tree, answer); 14 | return answer; 15 | } 16 | 17 | module.exports = inorder; 18 | -------------------------------------------------------------------------------- /js/isMirror.js: -------------------------------------------------------------------------------- 1 | function isMirror(a, b) { 2 | if (!a && !b) { 3 | return true; 4 | } 5 | if (!a || !b) { 6 | return false; 7 | } 8 | if (a.value !== b.value) { 9 | return false; 10 | } 11 | return isMirror(a.left, b.right) && isMirror(a.right, b.left); 12 | } 13 | 14 | module.exports = isMirror; 15 | -------------------------------------------------------------------------------- /js/isMirror.test.js: -------------------------------------------------------------------------------- 1 | const mirrorTree = require('./mirrorTree'); 2 | const isMirror = require('./isMirror'); 3 | 4 | test('trees', () => { 5 | let tree = { 6 | value: 1, 7 | left: { 8 | value: 2, 9 | left: { 10 | value: 3, 11 | }, 12 | right: { 13 | value: 'A', 14 | }, 15 | }, 16 | right: { 17 | value: 'B', 18 | }, 19 | }; 20 | 21 | let m = mirrorTree(tree); 22 | 23 | expect(isMirror(tree, m)).toBe(true); 24 | expect(isMirror(m, tree)).toBe(true); 25 | 26 | tree.left.right.value = 'X'; 27 | expect(isMirror(tree, m)).toBe(false); 28 | expect(isMirror(m, tree)).toBe(false); 29 | }); 30 | -------------------------------------------------------------------------------- /js/isValidNumber.js: -------------------------------------------------------------------------------- 1 | // Does allow trailing zeros 2 | function isValidPostDecimalPoint(str, i) { 3 | while (i < str.length) { 4 | if ('0123456789'.indexOf(str[i]) < 0) { 5 | return false; 6 | } 7 | i++; 8 | } 9 | return true; 10 | } 11 | 12 | // Allows leading zeros 13 | function isValidPositiveNumber(str, i) { 14 | if (i >= str.length) { 15 | return false; 16 | } 17 | if (i === str.length - 1 && str[i] === '.') { 18 | // It's only a deciaml point 19 | return false; 20 | } 21 | while (i < str.length) { 22 | if (str[i] === '.') { 23 | return isValidPostDecimalPoint(str, i + 1); 24 | } 25 | if ('0123456789'.indexOf(str[i]) < 0) { 26 | return false; 27 | } 28 | i++; 29 | } 30 | return true; 31 | } 32 | 33 | function isValidNumber(str) { 34 | if (!str || str.length < 1) { 35 | return false; 36 | } 37 | if (str[0] === '-') { 38 | return isValidPositiveNumber(str, 1); 39 | } else { 40 | return isValidPositiveNumber(str, 0); 41 | } 42 | } 43 | 44 | module.exports = isValidNumber; 45 | -------------------------------------------------------------------------------- /js/isValidNumber.test.js: -------------------------------------------------------------------------------- 1 | const isValidNumber = require('./isValidNumber'); 2 | 3 | function numberize(x) { 4 | if (isValidNumber(x)) { 5 | return x; 6 | } else { 7 | return 'nan'; 8 | } 9 | } 10 | 11 | function expectInvalid(x) { 12 | expect(numberize(x)).toBe('nan'); 13 | } 14 | 15 | test('basic', () => { 16 | expect(isValidNumber('13')).toBe(true); 17 | }); 18 | 19 | test('bad stuff', () => { 20 | expectInvalid('a'); 21 | expectInvalid('.'); 22 | expectInvalid('-'); 23 | expectInvalid('1e3'); 24 | expectInvalid('-.'); 25 | }); 26 | 27 | test('decimals', () => { 28 | expect(isValidNumber('0.123')).toBe(true); 29 | expect(isValidNumber('-0.89898')).toBe(true); 30 | expect(isValidNumber('.456')).toBe(true); 31 | expect(isValidNumber('-.777')).toBe(true); 32 | }); 33 | -------------------------------------------------------------------------------- /js/lastable.js: -------------------------------------------------------------------------------- 1 | const SortedMap = require('collections/sorted-map'); 2 | const DLL = require('./dll'); 3 | 4 | class Lastable { 5 | constructor() { 6 | // A lastable stores objects with three things: 7 | // key: the key 8 | // value: the value 9 | // id: each data access gets an id. ids go *down* over time. 10 | // this contains the lowest id for any of the accesses of this key 11 | // (ie the most recent one) 12 | 13 | // Maps key -> { value, node } 14 | // where node is a list node 15 | this.data = new Map(); 16 | 17 | // A list of all keys, from most recently used to least 18 | // null if there are none 19 | this.list = null; 20 | } 21 | 22 | prependHelper(key) { 23 | if (this.list) { 24 | this.list = this.list.prepend(key); 25 | } else { 26 | this.list = new DLL(key); 27 | } 28 | } 29 | 30 | removeHelper(node) { 31 | if (this.list === node) { 32 | this.list = node.next; 33 | } 34 | node.remove(); 35 | } 36 | 37 | put(key, value) { 38 | let blob = this.data.get(key); 39 | if (blob) { 40 | this.removeHelper(blob.node); 41 | } 42 | this.prependHelper(key); 43 | this.data.set(key, { value: value, node: this.list }); 44 | } 45 | 46 | get(key) { 47 | let blob = this.data.get(key); 48 | if (!blob) { 49 | throw new Error('nothing there for key: ' + key); 50 | } 51 | this.removeHelper(blob.node); 52 | this.prependHelper(key); 53 | blob.node = this.list; 54 | return blob.value; 55 | } 56 | 57 | del(key) { 58 | let blob = this.data.get(key); 59 | if (blob) { 60 | this.removeHelper(blob.node); 61 | this.data.delete(key); 62 | } 63 | } 64 | 65 | // Returns a key 66 | last() { 67 | return this.list.value; 68 | } 69 | } 70 | 71 | module.exports = Lastable; 72 | -------------------------------------------------------------------------------- /js/lastable.test.js: -------------------------------------------------------------------------------- 1 | let Lastable = require('./lastable'); 2 | 3 | test('pop it all', () => { 4 | let data = new Lastable(); 5 | data.put('a', 1); 6 | data.put('b', 2); 7 | data.put('c', 3); 8 | data.put('d', 4); 9 | data.get('a'); 10 | data.get('c'); 11 | data.get('a'); 12 | expect(data.last()).toBe('a'); 13 | data.del('a'); 14 | expect(data.last()).toBe('c'); 15 | data.del('c'); 16 | expect(data.last()).toBe('d'); 17 | }); 18 | 19 | test('puts on deleted keys', () => { 20 | let data = new Lastable(); 21 | data.put('a', 1); 22 | data.put('b', 2); 23 | data.del('a'); 24 | data.put('a', 3); 25 | data.put('c', 4); 26 | expect(data.last()).toBe('c'); 27 | data.del('c'); 28 | expect(data.last()).toBe('a'); 29 | data.del('a'); 30 | expect(data.last()).toBe('b'); 31 | }); 32 | 33 | test('lasting the empty thing', () => { 34 | let data = new Lastable(); 35 | let lasting = () => { 36 | data.last(); 37 | }; 38 | expect(lasting).toThrow(); 39 | }); 40 | 41 | test('get bumps', () => { 42 | let data = new Lastable(); 43 | data.put('a', 1); 44 | data.put('b', 1); 45 | expect(data.last()).toBe('b'); 46 | data.get('a'); 47 | expect(data.last()).toBe('a'); 48 | }); 49 | -------------------------------------------------------------------------------- /js/lowestCommonAncestor.js: -------------------------------------------------------------------------------- 1 | // All of these assume the tree has no duplicates 2 | 3 | // Returns an object with: 4 | // lca: the lca of the two nodes, if it's known 5 | // num: the number of value1 and value2 that is in the tree 6 | function helper(tree, value1, value2) { 7 | if (!tree) { 8 | return { 9 | num: 0, 10 | }; 11 | } 12 | let left = helper(tree.left, value1, value2); 13 | if (left.lca) { 14 | return left; 15 | } 16 | let right = helper(tree.right, value1, value2); 17 | if (right.lca) { 18 | return right; 19 | } 20 | let thisNum = (tree.value === value1 || tree.value === value2) ? 1 : 0; 21 | let num = left.num + right.num + thisNum; 22 | if (num >= 2) { 23 | return { 24 | lca: tree, 25 | num: num 26 | }; 27 | } 28 | return { 29 | num: num 30 | }; 31 | } 32 | 33 | // Returns the lowest common ancestor. 34 | function lca(tree, value1, value2) { 35 | let data = helper(tree, value1, value2); 36 | return data.lca; 37 | } 38 | 39 | module.exports = lca; 40 | -------------------------------------------------------------------------------- /js/lowestCommonAncestor.test.js: -------------------------------------------------------------------------------- 1 | let lca = require('./lowestCommonAncestor'); 2 | 3 | let tree = { 4 | value: 'D', 5 | left: { 6 | value: 'B', 7 | left: { 8 | value: 'A', 9 | }, 10 | right: { 11 | value: 'C', 12 | }, 13 | }, 14 | right: { 15 | value: 'F', 16 | left: { 17 | value: 'E', 18 | }, 19 | right: { 20 | value: 'G', 21 | }, 22 | }, 23 | }; 24 | 25 | test('lca', () => { 26 | let t = lca(tree, 'A', 'C'); 27 | expect(t.value).toBe('B'); 28 | }); 29 | -------------------------------------------------------------------------------- /js/makeTreeFromOrders.js: -------------------------------------------------------------------------------- 1 | // Makes a tree from an inorder and a preorder traversal. 2 | // Assumes all values are distinct 3 | // Note this could be more efficient with less list-copying 4 | function makeTree(inorder, preorder) { 5 | if (inorder.length !== preorder.length) { 6 | throw new Error('inorder length should equal preorder length'); 7 | } 8 | if (inorder.length === 0) { 9 | return null; 10 | } 11 | let rootValue = preorder[0]; 12 | let index = inorder.indexOf(rootValue); 13 | let leftInorder = inorder.slice(0, index); 14 | let rightInorder = inorder.slice(index + 1); 15 | let leftPreorder = preorder.slice(1, index + 1); 16 | let rightPreorder = preorder.slice(index + 1); 17 | return { 18 | value: rootValue, 19 | left: makeTree(leftInorder, leftPreorder), 20 | right: makeTree(rightInorder, rightPreorder), 21 | }; 22 | } 23 | 24 | module.exports = makeTree; 25 | -------------------------------------------------------------------------------- /js/makeTreeFromOrders.test.js: -------------------------------------------------------------------------------- 1 | const inorder = require('./inorder'); 2 | const preorder = require('./preorder'); 3 | const makeTree = require('./makeTreeFromOrders'); 4 | const treeEqual = require('./treeEqual'); 5 | 6 | let tree = { 7 | value: 'A', 8 | left: { 9 | value: 'B', 10 | left: { value: 'C' }, 11 | right: { value: 'D' }, 12 | }, 13 | right: { 14 | value: 'E', 15 | left: { value: 'F' }, 16 | right: { value: 'G' }, 17 | }, 18 | }; 19 | 20 | function testTree(tree) { 21 | let inorderList = inorder(tree); 22 | let preorderList = preorder(tree); 23 | let tree2 = makeTree(inorderList, preorderList); 24 | expect(treeEqual(tree, tree2)).toBe(true); 25 | } 26 | 27 | test('making trees', () => { 28 | testTree(tree); 29 | testTree(null); 30 | testTree(tree.left); 31 | testTree(tree.right); 32 | }); 33 | -------------------------------------------------------------------------------- /js/maxConnectedSum.js: -------------------------------------------------------------------------------- 1 | // Code to find the maximum sum of any connected subgraph of a tree. 2 | 3 | // Returns an object with: 4 | // root: the max connected sum including the root node 5 | // any: the max connected sum of any subgroup 6 | function helper(tree) { 7 | if (!tree) { 8 | return { 9 | root: 0, 10 | any: 0, 11 | }; 12 | } 13 | 14 | let left = helper(tree.left); 15 | let right = helper(tree.right); 16 | 17 | // Figure out the max connected sum including the root node 18 | let root = tree.value; 19 | if (left.root > 0) { 20 | root += left.root; 21 | } 22 | if (right.root > 0) { 23 | root += right.root; 24 | } 25 | 26 | return { 27 | root: root, 28 | any: Math.max(left.any, right.any, root), 29 | }; 30 | } 31 | 32 | function maxConnectedSum(tree) { 33 | return helper(tree).any; 34 | } 35 | 36 | module.exports = maxConnectedSum; 37 | -------------------------------------------------------------------------------- /js/maxConnectedSum.test.js: -------------------------------------------------------------------------------- 1 | const maxConnectedSum = require('./maxConnectedSum'); 2 | 3 | let tree = { 4 | value: 1, 5 | left: { 6 | value: -2, 7 | left: { 8 | value: 10, 9 | }, 10 | right: { 11 | value: 1, 12 | }, 13 | }, 14 | right: { 15 | value: 10, 16 | left: { 17 | value: -1, 18 | }, 19 | right: { 20 | value: 1, 21 | }, 22 | }, 23 | }; 24 | 25 | test('max connected sums', () => { 26 | expect(maxConnectedSum(tree)).toBe(21); 27 | expect(maxConnectedSum(tree.left)).toBe(10); 28 | expect(maxConnectedSum(tree.right)).toBe(11); 29 | expect(maxConnectedSum(tree.right.left)).toBe(0); 30 | }); 31 | -------------------------------------------------------------------------------- /js/mirrorTree.js: -------------------------------------------------------------------------------- 1 | function mirrorTree(tree) { 2 | if (!tree) { 3 | return null; 4 | } 5 | return { 6 | value: tree.value, 7 | left: mirrorTree(tree.right), 8 | right: mirrorTree(tree.left), 9 | }; 10 | } 11 | 12 | module.exports = mirrorTree; 13 | -------------------------------------------------------------------------------- /js/numInterpret.js: -------------------------------------------------------------------------------- 1 | // We have a coding system from letters to numbers where a=1, b=2, ...z=26. 2 | // numInterpret takes a string of digits as an input and computes 3 | // the number of valid interpretations of the input. 4 | function numInterpret(s) { 5 | let cache = new Map(); 6 | // Helper uses the cache and returns the number of interpretations 7 | // for the string containing indices 0..i 8 | // So when i = -1 it's the empty string 9 | let helper = (i) => { 10 | if (i === -1) { 11 | return 1; 12 | } 13 | if (i < -1) { 14 | throw new Error('i should not be ' + i); 15 | } 16 | if (cache.has(i)) { 17 | return cache.get(i); 18 | } 19 | 20 | let answer = 0; 21 | // Check if s[i] is itself a legit interpretation 22 | if ('123456789'.indexOf(s[i]) >= 0) { 23 | answer += helper(i - 1); 24 | } 25 | 26 | // Check if s[i-1] s[i] makes a two-digit interpretation 27 | // The '.0' avoids things that already have a decimal point 28 | let num = Number(s[i - 1] + s[i] + '.0'); 29 | if (num >= 10 && num <= 26) { 30 | answer += helper(i - 2); 31 | } 32 | 33 | cache.set(i, answer); 34 | return answer; 35 | }; 36 | 37 | return helper(s.length - 1); 38 | } 39 | 40 | module.exports = numInterpret; 41 | -------------------------------------------------------------------------------- /js/numInterpret.test.js: -------------------------------------------------------------------------------- 1 | const numInterpret = require('./numInterpret'); 2 | 3 | test('basic functionality', () => { 4 | expect(numInterpret('1')).toBe(1); 5 | expect(numInterpret('11')).toBe(2); 6 | expect(numInterpret('111')).toBe(3); 7 | expect(numInterpret('1111')).toBe(5); 8 | }); 9 | 10 | test('stuff that should not work', () => { 11 | expect(numInterpret('a')).toBe(0); 12 | expect(numInterpret('0123123123')).toBe(0); 13 | expect(numInterpret('11113031111')).toBe(0); 14 | }); 15 | 16 | test('single pathers', () => { 17 | expect(numInterpret('99999999')).toBe(1); 18 | expect(numInterpret('39465738946578349657843')).toBe(1); 19 | }); 20 | -------------------------------------------------------------------------------- /js/preorder.js: -------------------------------------------------------------------------------- 1 | // Appends the preorder onto output 2 | function preorderHelper(tree, output) { 3 | if (!tree) { 4 | return; 5 | } 6 | output.push(tree.value); 7 | preorderHelper(tree.left, output); 8 | preorderHelper(tree.right, output); 9 | } 10 | 11 | function preorder(tree) { 12 | let answer = []; 13 | preorderHelper(tree, answer); 14 | return answer; 15 | } 16 | 17 | module.exports = preorder; 18 | -------------------------------------------------------------------------------- /js/preorder.test.js: -------------------------------------------------------------------------------- 1 | const preorder = require('./preorder'); 2 | 3 | test('basic functionality', () => { 4 | expect(False).toBe(True); // TODO 5 | }); 6 | 7 | -------------------------------------------------------------------------------- /js/reverseList.js: -------------------------------------------------------------------------------- 1 | // Reverse a linked list. 2 | // A linked list is an object with two fields: 3 | // value 4 | // next 5 | // Next is null at the end of the list. 6 | // This does not mutate the argument 7 | function reverse(linkedList) { 8 | let answer = null; 9 | while (linkedList !== null) { 10 | answer = { 11 | value: linkedList.value, 12 | next: answer, 13 | }; 14 | linkedList = linkedList.next; 15 | } 16 | return answer; 17 | } 18 | 19 | module.exports = reverse; 20 | -------------------------------------------------------------------------------- /js/reverseList.test.js: -------------------------------------------------------------------------------- 1 | let reverse = require('./reverseList'); 2 | 3 | test('reversing null is null', () => { 4 | expect(reverse(null)).toBeNull(); 5 | }); 6 | 7 | test('reversing a linked list', () => { 8 | let list = { 9 | value: 1, 10 | next: { 11 | value: 2, 12 | next: { 13 | value: 3, 14 | next: null, 15 | }, 16 | }, 17 | }; 18 | let goal = { 19 | value: 3, 20 | next: { 21 | value: 2, 22 | next: { 23 | value: 1, 24 | next: null, 25 | }, 26 | }, 27 | }; 28 | expect(reverse(list)).toEqual(goal); 29 | }); 30 | -------------------------------------------------------------------------------- /js/sampler.js: -------------------------------------------------------------------------------- 1 | // A Sampler lets you add objects with weights. 2 | // You can then call "sample" which returns one of the objects 3 | // at random. 4 | class Sampler { 5 | constructor() { 6 | this.numItems = 0; 7 | this.totalWeight = 0; 8 | 9 | // Used iff we have only one item 10 | this.item = null; 11 | 12 | // Used recursively if we have more than one item 13 | this.left = null; 14 | this.right = null; 15 | } 16 | 17 | add(item, weight) { 18 | if (weight <= 0) { 19 | throw new Error('weight cannot be ' + weight); 20 | } 21 | 22 | if (this.numItems === 0) { 23 | this.numItems = 1; 24 | this.totalWeight = weight; 25 | this.item = item; 26 | return; 27 | } 28 | 29 | if (this.numItems === 1) { 30 | // Time to split this node into two nodes 31 | this.left = new Sampler(); 32 | this.right = new Sampler(); 33 | this.left.add(this.item, this.totalWeight); 34 | this.right.add(item, weight); 35 | 36 | this.numItems = 2; 37 | this.totalWeight += weight; 38 | this.item = null; 39 | return; 40 | } 41 | 42 | // Recurse 43 | this.numItems += 1; 44 | this.totalWeight += weight; 45 | if (this.left.numItems <= this.right.numItems) { 46 | this.left.add(item, weight); 47 | } else { 48 | this.right.add(item, weight); 49 | } 50 | } 51 | 52 | sample(randomIndex) { 53 | if (randomIndex == null) { 54 | randomIndex = Math.random(); 55 | } 56 | if (this.numItems === 0) { 57 | throw new Error('cannot randomly sample from an empty thing'); 58 | } 59 | if (this.numItems === 1) { 60 | return this.item; 61 | } 62 | let val = randomIndex * this.totalWeight; 63 | if (val < this.left.totalWeight) { 64 | return this.left.sample(val / this.left.totalWeight); 65 | } else { 66 | return this.right.sample( 67 | (val - this.left.totalWeight) / this.right.totalWeight); 68 | } 69 | } 70 | } 71 | 72 | module.exports = Sampler; 73 | -------------------------------------------------------------------------------- /js/sampler.test.js: -------------------------------------------------------------------------------- 1 | const Sampler = require('./sampler'); 2 | 3 | test('five things', () => { 4 | let s = new Sampler(); 5 | s.add('A', 10); 6 | s.add('B', 10); 7 | s.add('C', 10); 8 | s.add('D', 10); 9 | s.add('E', 10); 10 | let samples = [ 11 | s.sample(0.1), 12 | s.sample(0.3), 13 | s.sample(0.5), 14 | s.sample(0.7), 15 | s.sample(0.9), 16 | ]; 17 | samples.sort(); 18 | expect(samples.join('')).toBe('ABCDE'); 19 | }); 20 | -------------------------------------------------------------------------------- /js/sequenceSum.js: -------------------------------------------------------------------------------- 1 | // Given a sequence of positive integers seq and an integer total, 2 | // return whether a contiguous sequence of seq sums up to total 3 | function sequenceSum(seq, sum) { 4 | if (sum === 0) { 5 | return true; 6 | } 7 | 8 | // Sums that start at the beginning 9 | let partials = new Set(); 10 | partials.add(0); 11 | let accum = 0; 12 | for (let integer of seq) { 13 | accum += integer; 14 | if (partials.has(accum - sum)) { 15 | return true; 16 | } 17 | partials.add(accum); 18 | } 19 | return false; 20 | } 21 | 22 | module.exports = sequenceSum; 23 | -------------------------------------------------------------------------------- /js/sequenceSum.test.js: -------------------------------------------------------------------------------- 1 | const sequenceSum = require('./sequenceSum'); 2 | 3 | test('a sum that exists', () => { 4 | expect(sequenceSum([1, 2, 3, 4, 5, 6], 9)).toBe(true); 5 | }); 6 | 7 | test('a sum that does not exist', () => { 8 | expect(sequenceSum([2, 4, 6, 8, 10, 12, 14, 16, 18, 20], 21)).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /js/sqrt.js: -------------------------------------------------------------------------------- 1 | function sqrt(x) { 2 | let epsilon = 0.0000001; 3 | if (x < 0) { 4 | throw new Error('cant do sqrt of ' + x); 5 | } 6 | let a = 0; 7 | let b = 1; 8 | while (Math.abs(a - b) > epsilon) { 9 | a = b; 10 | b = (b + x/b) / 2; 11 | } 12 | return b; 13 | } 14 | 15 | module.exports = sqrt; 16 | -------------------------------------------------------------------------------- /js/sqrt.test.js: -------------------------------------------------------------------------------- 1 | const sqrt = require('./sqrt'); 2 | 3 | function testSqrtOf(x) { 4 | let s = sqrt(x); 5 | expect(Math.abs(s * s - x)).toBeLessThan(0.001); 6 | } 7 | 8 | test('sqrting things', () => { 9 | testSqrtOf(1); 10 | testSqrtOf(0); 11 | testSqrtOf(30000); 12 | }); 13 | -------------------------------------------------------------------------------- /js/subgridCounter.js: -------------------------------------------------------------------------------- 1 | // Given a large rectangular 2D grid of arbitrarily-placed 1's and 0's, 2 | // create a service that answers the query "how many 1's in a given subgrid?". 3 | // Coordinates are "math-style" where x is the first one, y is the second. 4 | class SubgridCounter { 5 | constructor(grid) { 6 | this.partial = new Map(); 7 | let maxX = grid.length - 1; 8 | let maxY = grid[0].length - 1; 9 | for (let x = 0; x <= maxX; x++) { 10 | for (let y = 0; y <= maxY; y++) { 11 | let answer = ( 12 | grid[x][y] + 13 | this._get(x - 1, y) + this._get(x, y - 1) - this._get(x - 1, y - 1)); 14 | this._set(x, y, answer); 15 | } 16 | } 17 | } 18 | 19 | // Gets the count of the subgrid 0, 0, x, y 20 | _get(x, y) { 21 | if (x < 0 || y < 0) { 22 | return 0; 23 | } 24 | let key = x + ',' + y; 25 | return this.partial.get(key); 26 | } 27 | 28 | // Sets the count of the subgrid 0, 0, x, y 29 | _set(x, y, count) { 30 | let key = x + ',' + y; 31 | this.partial.set(key, count); 32 | } 33 | 34 | count(minX, minY, maxX, maxY) { 35 | return ( 36 | this._get(maxX, maxY) - this._get(minX - 1, maxY) 37 | - this._get(maxX, minY - 1) + this._get(minX - 1, minY - 1)); 38 | } 39 | } 40 | 41 | module.exports = SubgridCounter; 42 | -------------------------------------------------------------------------------- /js/subgridCounter.test.js: -------------------------------------------------------------------------------- 1 | const SubgridCounter = require('./subgridCounter'); 2 | 3 | test('basic subgrid', () => { 4 | let data = [ 5 | [1, 0, 1], 6 | [0, 1, 0], 7 | [1, 0, 1], 8 | ]; 9 | let counter = new SubgridCounter(data); 10 | expect(counter.count(0, 0, 0, 0)).toBe(1); 11 | expect(counter.count(1, 1, 1, 1)).toBe(1); 12 | expect(counter.count(0, 1, 2, 2)).toBe(3); 13 | }); 14 | 15 | test('modifying the grid', () => { 16 | let data = [[1]]; 17 | let counter1 = new SubgridCounter(data); 18 | expect(counter1.count(0, 0, 0, 0)).toBe(1); 19 | data[0][0] = 2; 20 | let counter2 = new SubgridCounter(data); 21 | expect(counter2.count(0, 0, 0, 0)).toBe(2); 22 | expect(counter1.count(0, 0, 0, 0)).toBe(1); 23 | }); 24 | -------------------------------------------------------------------------------- /js/threeSum.js: -------------------------------------------------------------------------------- 1 | // Returns whether any three items in this array sum to zero. 2 | function threeSum(array) { 3 | let twoSums = new Set(); 4 | for (let i = 0; i < array.length; i++) { 5 | for (let j = i + 1; j < array.length; j++) { 6 | twoSums.add(array[i] + array[j]); 7 | } 8 | } 9 | for (let x of array) { 10 | if (twoSums.has(-x)) { 11 | return true; 12 | } 13 | } 14 | return false; 15 | } 16 | 17 | module.exports = threeSum; 18 | 19 | // TODO: test 20 | -------------------------------------------------------------------------------- /js/threeSum.test.js: -------------------------------------------------------------------------------- 1 | let threeSum = require('./threeSum'); 2 | 3 | test('sums', () => { 4 | expect(threeSum([1, 2, 3, 4, 5, 6, 7])).toBe(false); 5 | expect(threeSum([1, 2, 3, -12, 4, 5, 6, 7])).toBe(true); 6 | expect(threeSum([-9, -7, -5, -3, -1, 1, 3, 5, 7, 9])).toBe(false); 7 | expect(threeSum([-3, -2, -1, 1, 2, 3])).toBe(true); 8 | }); 9 | -------------------------------------------------------------------------------- /js/topWords.js: -------------------------------------------------------------------------------- 1 | // Take input from a document and present the top N most frequent words and 2 | // their counts within the document. 3 | // doc is just a string of text. 4 | // Returns a length-n list of [word, count] pairs. 5 | function topWords(doc, n) { 6 | // Could use more punctuation 7 | let words = doc.toLowerCase().split(/[ \.,]+/); 8 | let counts = {}; 9 | for (let word of words) { 10 | if (word.length > 0) { 11 | counts[word] = (counts[word] || 0) + 1; 12 | } 13 | } 14 | let pairs = []; 15 | for (let word in counts) { 16 | pairs.push([word, counts[word]]); 17 | } 18 | pairs.sort(comparePair); 19 | return pairs.slice(0, n); 20 | } 21 | 22 | function comparePair(pair1, pair2) { 23 | let [word1, count1] = pair1; 24 | let [word2, count2] = pair2; 25 | return count2 - count1; 26 | } 27 | 28 | module.exports = topWords; 29 | -------------------------------------------------------------------------------- /js/topWords.test.js: -------------------------------------------------------------------------------- 1 | const topWords = require('./topWords'); 2 | 3 | test('basic usage', () => { 4 | let doc = 'a b c d e f g a b c a b a'; 5 | let top = topWords(doc, 3); 6 | expect(top).toEqual([ 7 | ['a', 4], 8 | ['b', 3], 9 | ['c', 2], 10 | ]); 11 | }); 12 | 13 | test('with punctuation', () => { 14 | let doc = 'Bonk, and bonk.'; 15 | let top = topWords(doc, 3); 16 | expect(top).toEqual([ 17 | ['bonk', 2], 18 | ['and', 1], 19 | ]); 20 | }); 21 | -------------------------------------------------------------------------------- /js/treeEqual.js: -------------------------------------------------------------------------------- 1 | function equal(tree1, tree2) { 2 | if (!tree1 && !tree2) { 3 | return true; 4 | } 5 | if (!tree1 || !tree2) { 6 | return false; 7 | } 8 | if (tree1.value !== tree2.value) { 9 | return false; 10 | } 11 | return equal(tree1.left, tree2.left) && equal(tree1.right, tree2.right); 12 | } 13 | 14 | module.exports = equal; 15 | -------------------------------------------------------------------------------- /js/treeIsSorted.js: -------------------------------------------------------------------------------- 1 | // Returns an object with 2 | // min: the min value of the tree 3 | // sorted: whether the tree is sorted 4 | // max: the max value of the tree 5 | // If sorted is false the others aren't needed 6 | function helper(tree) { 7 | if (!tree) { 8 | return { sorted: true }; 9 | } 10 | let left = helper(tree.left); 11 | if (!left.sorted || left.max > tree.value) { 12 | return { sorted: false }; 13 | } 14 | let right = helper(tree.right); 15 | if (!right.sorted || right.min < tree.value) { 16 | return { sorted: false }; 17 | } 18 | return { 19 | min: left.min || tree.value, 20 | sorted: true, 21 | max: right.max || tree.value, 22 | }; 23 | } 24 | 25 | function isSorted(tree) { 26 | let data = helper(tree); 27 | return data.sorted; 28 | } 29 | 30 | module.exports = isSorted; 31 | -------------------------------------------------------------------------------- /js/treeIsSorted.test.js: -------------------------------------------------------------------------------- 1 | let isSorted = require('./treeIsSorted'); 2 | 3 | test('normal tree', () => { 4 | let tree = { 5 | left: { 6 | left: { value: 'A' }, 7 | value: 'B', 8 | right: { value: 'C' }, 9 | }, 10 | value: 'D', 11 | right: { 12 | left: { value: 'E' }, 13 | value: 'F', 14 | right: { value: 'G' }, 15 | }, 16 | }; 17 | expect(isSorted(tree)).toBe(true); 18 | 19 | tree.left.right.value = 'Z'; 20 | expect(isSorted(tree)).toBe(false); 21 | }); 22 | -------------------------------------------------------------------------------- /js/treeIterator.js: -------------------------------------------------------------------------------- 1 | class TreeIterator { 2 | constructor(root) { 3 | this.stack = []; 4 | while (root) { 5 | this.stack.push(root); 6 | root = root.left; 7 | } 8 | } 9 | 10 | endOfPath() { 11 | return this.stack[this.stack.length - 1]; 12 | } 13 | 14 | // Throws an error when there is no next value, 15 | // same error as popping from an empty list 16 | nextValue() { 17 | let current = this.stack.pop(); 18 | if (!current) { 19 | throw new Error('end of iteration'); 20 | } 21 | let returnValue = current.value; 22 | if (current.right) { 23 | let node = current.right; 24 | while (node) { 25 | this.stack.push(node); 26 | node = node.left; 27 | } 28 | } 29 | return returnValue; 30 | } 31 | } 32 | 33 | module.exports = TreeIterator; 34 | 35 | -------------------------------------------------------------------------------- /js/treeIterator.test.js: -------------------------------------------------------------------------------- 1 | let TreeIterator = require('./treeIterator'); 2 | 3 | test('normal tree', () => { 4 | let tree = { 5 | left: { 6 | left: { value: 'A' }, 7 | value: 'B', 8 | right: { value: 'C' }, 9 | }, 10 | value: 'D', 11 | right: { 12 | left: { value: 'E' }, 13 | value: 'F', 14 | right: { value: 'G' }, 15 | }, 16 | }; 17 | let iter = new TreeIterator(tree); 18 | expect(iter.nextValue()).toBe('A'); 19 | expect(iter.nextValue()).toBe('B'); 20 | expect(iter.nextValue()).toBe('C'); 21 | expect(iter.nextValue()).toBe('D'); 22 | expect(iter.nextValue()).toBe('E'); 23 | expect(iter.nextValue()).toBe('F'); 24 | expect(iter.nextValue()).toBe('G'); 25 | expect(() => iter.nextValue()).toThrow(); 26 | }); 27 | -------------------------------------------------------------------------------- /js/treeLevelMatch.js: -------------------------------------------------------------------------------- 1 | // Returns the next level in a binary tree given the current level 2 | function nextLevel(level) { 3 | let answer = []; 4 | for (let node of level) { 5 | if (node.left) { 6 | answer.push(node.left); 7 | } 8 | if (node.right) { 9 | answer.push(node.right); 10 | } 11 | } 12 | return answer; 13 | } 14 | 15 | // Checks if the two trees have the same contents at each level. 16 | function levelMatch(tree1, tree2) { 17 | let level1 = [tree1]; 18 | let level2 = [tree2]; 19 | while (level1.length !== 0 && level2.length !== 0) { 20 | if (level1.length !== level2.length) { 21 | return false; 22 | } 23 | for (let i = 0; i < level1.length; i++) { 24 | if (level1[i].value !== level2[i].value) { 25 | return false; 26 | } 27 | } 28 | level1 = nextLevel(level1); 29 | level2 = nextLevel(level2); 30 | } 31 | return true; 32 | } 33 | 34 | module.exports = levelMatch; 35 | -------------------------------------------------------------------------------- /js/treeLevelMatch.test.js: -------------------------------------------------------------------------------- 1 | const levelMatch = require('./treeLevelMatch'); 2 | 3 | test('equal trees', () => { 4 | let one = { 5 | value: 'A', 6 | left: { 7 | value: 'B', 8 | left: { 9 | value: 'C', 10 | }, 11 | right: { 12 | value: 'D', 13 | } 14 | } 15 | }; 16 | 17 | let two = { 18 | value: 'A', 19 | right: { 20 | value: 'B', 21 | left: { 22 | value: 'C', 23 | }, 24 | right: { 25 | value: 'D', 26 | } 27 | } 28 | }; 29 | 30 | expect(levelMatch(one, two)).toBe(true); 31 | }); 32 | -------------------------------------------------------------------------------- /js/treeLongestPath.js: -------------------------------------------------------------------------------- 1 | // Returns an object with: 2 | // longestPath = the length of the longest path between node1 and node2 3 | // Otherwise: 4 | // depth1 = the depth of node1, if it's there 5 | // depth2 = the depth of node2, if it's there 6 | function helper(tree, node1, node2) { 7 | if (node1 === node2) { 8 | return { 9 | longestPath: 0, 10 | }; 11 | } 12 | if (!tree) { 13 | return {}; 14 | } 15 | if (tree === node1) { 16 | return { 17 | depth1: 0, 18 | }; 19 | } 20 | if (tree === node2) { 21 | return { 22 | depth2: 0, 23 | }; 24 | } 25 | 26 | let leftData = helper(tree.left, node1, node2); 27 | if (leftData.longestPath != null) { 28 | return leftData; 29 | } 30 | let rightData = helper(tree.right, node1, node2); 31 | if (rightData.longestPath != null) { 32 | return rightData; 33 | } 34 | 35 | let depth1 = (leftData.depth1 == null) ? rightData.depth1 : leftData.depth1; 36 | let depth2 = (leftData.depth2 == null) ? rightData.depth2 : leftData.depth2; 37 | let answer = {}; 38 | if (depth1 != null) { 39 | answer.depth1 = depth1 + 1; 40 | } 41 | if (depth2 != null) { 42 | answer.depth2 = depth2 + 1; 43 | } 44 | if (answer.depth1 != null && answer.depth2 != null) { 45 | return { 46 | longestPath: answer.depth1 + answer.depth2, 47 | }; 48 | } 49 | return answer; 50 | } 51 | 52 | 53 | function longestPath(tree, node1, node2) { 54 | let data = helper(tree, node1, node2); 55 | if (data.longestPath != null) { 56 | return data.longestPath; 57 | } 58 | return -1; 59 | } 60 | 61 | module.exports = longestPath; 62 | -------------------------------------------------------------------------------- /js/treeLongestPath.test.js: -------------------------------------------------------------------------------- 1 | let longestPath = require('./treeLongestPath'); 2 | 3 | let x = { 4 | value: 'X', 5 | }; 6 | let y = { 7 | value: 'Y', 8 | }; 9 | 10 | test('sibling', () => { 11 | let tree = { 12 | left: x, 13 | right: y, 14 | }; 15 | expect(longestPath(tree, x, y)).toBe(2); 16 | }); 17 | 18 | test('uncle', () => { 19 | let tree = { 20 | left: x, 21 | right: { 22 | left: {}, 23 | right: y, 24 | }, 25 | }; 26 | 27 | expect(longestPath(tree, x, y)).toBe(3); 28 | }); 29 | 30 | test('impossible', () => { 31 | let tree = { 32 | left: x, 33 | right: x, 34 | }; 35 | expect(longestPath(tree, x, y)).toBe(-1); 36 | }); 37 | -------------------------------------------------------------------------------- /js/treeMarkDistance.js: -------------------------------------------------------------------------------- 1 | // Given the root of a binary tree with 2 marked nodes, determine the 2 | // length of the path between the 2 marked nodes. 3 | // marked nodes are just ones with "mark: true". 4 | 5 | // This recursive function returns info to help solve the problem. 6 | // If there are two marked nodes it returns { answer: answer }. 7 | // If there is only one marked node it returns { depth: depth }. 8 | // If there are no marked nodes it returns { empty: true }. 9 | // Assumes there are at most two marked nodes. 10 | function helper(tree) { 11 | if (!tree) { 12 | return { empty: true }; 13 | } 14 | let left = helper(tree.left); 15 | if (left.answer) { 16 | return left; 17 | } 18 | let right = helper(tree.right); 19 | if (right.answer) { 20 | return right; 21 | } 22 | 23 | if (left.empty && right.empty) { 24 | if (tree.mark) { 25 | return { depth: 0 }; 26 | } else { 27 | return { empty: true }; 28 | } 29 | } 30 | 31 | if (left.depth !== undefined && right.depth !== undefined) { 32 | return { answer: 2 + left.depth + right.depth }; 33 | } 34 | 35 | // The last case: there is precisely one marked node between left and 36 | // right subtrees 37 | let treeWithMark = left.empty ? right : left; 38 | if (tree.mark) { 39 | return { answer: treeWithMark.depth + 1 }; 40 | } else { 41 | return { depth: treeWithMark.depth + 1 }; 42 | } 43 | } 44 | 45 | function treeMarkDistance(tree) { 46 | let data = helper(tree); 47 | if (data.answer === undefined) { 48 | return -1; 49 | } 50 | return data.answer; 51 | } 52 | 53 | module.exports = treeMarkDistance; 54 | -------------------------------------------------------------------------------- /js/treeMarkDistance.test.js: -------------------------------------------------------------------------------- 1 | const treeMarkDistance = require('./treeMarkDistance'); 2 | 3 | test('cousins', () => { 4 | let tree = { 5 | left: { 6 | right: { 7 | mark: true, 8 | } 9 | }, 10 | right: { 11 | left: { 12 | mark: true, 13 | } 14 | } 15 | }; 16 | expect(treeMarkDistance(tree)).toBe(4); 17 | }); 18 | 19 | test('grandparent', () => { 20 | let tree = { 21 | left: { 22 | mark: true, 23 | right: { 24 | right: { 25 | mark: true, 26 | } 27 | } 28 | }, 29 | right: { 30 | left: { 31 | left: {}, 32 | right: {}, 33 | }, 34 | right: { 35 | left: {}, 36 | right: {}, 37 | } 38 | } 39 | }; 40 | expect(treeMarkDistance(tree)).toBe(2); 41 | }) 42 | -------------------------------------------------------------------------------- /js/treeToList.js: -------------------------------------------------------------------------------- 1 | // Converts a tree to a doubly-linked list with the provided left and right 2 | // nodes. 3 | // left and right and tree can all be null 4 | function helper(tree, left, right) { 5 | // Base case 6 | if (!tree) { 7 | if (left) { 8 | left.right = right; 9 | } 10 | if (right) { 11 | right.left = left; 12 | } 13 | return; 14 | } 15 | 16 | // Handle the left side 17 | if (!tree.left) { 18 | tree.left = left; 19 | if (left) { 20 | left.right = tree; 21 | } 22 | } else { 23 | helper(tree.left, left, tree); 24 | } 25 | 26 | // Handle the right side 27 | if (!tree.right) { 28 | tree.right = right; 29 | if (right) { 30 | right.left = tree; 31 | } 32 | } else { 33 | helper(tree.right, tree, right); 34 | } 35 | } 36 | 37 | // Converts a tree to a doubly-linked list 38 | function treeToList(tree) { 39 | helper(tree, null, null); 40 | } 41 | 42 | module.exports = treeToList; 43 | 44 | // TODO: test 45 | -------------------------------------------------------------------------------- /js/treeToList.test.js: -------------------------------------------------------------------------------- 1 | let treeToList = require('./treeToList'); 2 | 3 | // Checks that list is a doubly-linked-list with the given items. 4 | function checkList(list, items) { 5 | let node = list; 6 | while (node.left !== null) { 7 | node = node.left; 8 | } 9 | 10 | let prev = null; 11 | for (let item of items) { 12 | expect(node.left).toBe(prev); 13 | expect(node.value).toBe(item); 14 | prev = node; 15 | node = node.right; 16 | } 17 | expect(node).toBe(null); 18 | } 19 | 20 | function makeTree() { 21 | return { 22 | value: 'D', 23 | left: { 24 | value: 'B', 25 | left: { 26 | value: 'A', 27 | }, 28 | right: { 29 | value: 'C', 30 | }, 31 | }, 32 | right: { 33 | value: 'F', 34 | left: { 35 | value: 'E', 36 | }, 37 | right: { 38 | value: 'G', 39 | }, 40 | }, 41 | }; 42 | } 43 | 44 | test('tree to list', () => { 45 | let tree = makeTree(); 46 | treeToList(tree); 47 | checkList(tree, ['A', 'B', 'C', 'D', 'E', 'F', 'G']); 48 | }); 49 | -------------------------------------------------------------------------------- /js/volcano.js: -------------------------------------------------------------------------------- 1 | var CACHE = {}; 2 | 3 | // Sizes is an array of ints representing the size of minions. 4 | // Returns an array of probabilities that each one will be the last standing. 5 | function volcano(sizes) { 6 | if (sizes.length == 1) { 7 | return [1.0]; 8 | } 9 | 10 | if (sizes.length == 0) { 11 | throw new Error("cannot run volcano on the empty list"); 12 | } 13 | 14 | let key = "" + sizes; 15 | if (CACHE[key]) { 16 | return CACHE[key]; 17 | } 18 | 19 | // Recurse if anything is a zero 20 | for (let i = 0; i < sizes.length; i++) { 21 | if (sizes[i] == 0) { 22 | let smaller = sizes.slice(0, i).concat(sizes.slice(i + 1)); 23 | let answer = volcano(smaller); 24 | return answer 25 | .slice(0, i) 26 | .concat([0]) 27 | .concat(answer.slice(i)); 28 | } 29 | } 30 | 31 | // Sum up each possibility 32 | let answer = []; 33 | for (let size of sizes) { 34 | answer.push(0); 35 | } 36 | for (let i = 0; i < sizes.length; i++) { 37 | let copy = sizes.concat([]); 38 | copy[i] -= 1; 39 | let subanswer = volcano(copy); 40 | for (let j = 0; j < subanswer.length; j++) { 41 | answer[j] += (1.0 / sizes.length) * subanswer[j]; 42 | } 43 | } 44 | CACHE[key] = answer; 45 | return answer; 46 | } 47 | 48 | console.log(volcano([4, 4, 8])); 49 | -------------------------------------------------------------------------------- /js/volcano.test.js: -------------------------------------------------------------------------------- 1 | const volcano = require("./volcano"); 2 | 3 | test("basic usage", () => { 4 | let response = volcano([4, 4, 4, 4]); 5 | expect(response[0]).toBeCloseTo(0.25, 4); 6 | expect(response[1]).toBeCloseTo(0.25, 4); 7 | expect(response[2]).toBeCloseTo(0.25, 4); 8 | expect(response[3]).toBeCloseTo(0.25, 4); 9 | 10 | response = volcano([1, 2]); 11 | expect(response[1]).toBeGreaterThan(response[0]); 12 | }); 13 | -------------------------------------------------------------------------------- /lean/.gitignore: -------------------------------------------------------------------------------- 1 | *.olean 2 | /_target 3 | /leanpkg.path 4 | -------------------------------------------------------------------------------- /lean/leanpkg.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hellolean" 3 | version = "0.1" 4 | lean_version = "leanprover-community/lean:3.20.0" 5 | path = "src" 6 | 7 | [dependencies] 8 | mathlib = {git = "https://github.com/leanprover-community/mathlib", rev = "ae5b55bdf3f4cab4681fa54de75dfb8772b666fd"} 9 | -------------------------------------------------------------------------------- /lean/src/tutorial.lean: -------------------------------------------------------------------------------- 1 | import data.nat.basic 2 | import data.set.basic 3 | import logic.basic 4 | import tactic.basic 5 | import tactic.suggest 6 | open classical 7 | 8 | -- So that we can ignore the difference between computable and uncomputable sets. 9 | -- open_locale classical 10 | -- noncomputable theory 11 | 12 | def is_even (a : ℕ) := ∃ b, 2 * b = a 13 | 14 | def is_odd (a : ℕ) := ∃ b, 2 * b + 1 = a 15 | 16 | theorem zero_is_even: is_even 0 := 17 | exists.intro 0 (zero_mul 2) 18 | 19 | theorem even_plus_one_is_odd (a : ℕ) (h: is_even a) : is_odd (a + 1) := 20 | exists.elim h 21 | (assume b, assume hb: 2 * b = a, show is_odd (a + 1), from 22 | exists.intro b 23 | (calc 24 | 2 * b + 1 = a + 1 : by rw hb 25 | ) 26 | ) 27 | 28 | lemma two_times_one: 2 * 1 = 1 + 1 := rfl 29 | 30 | theorem odd_plus_one_is_even (a : ℕ) (h: is_odd a) : is_even (a + 1) := 31 | exists.elim h 32 | (assume b, assume hb: 2 * b + 1 = a, show is_even (a + 1), from 33 | exists.intro (b + 1) 34 | (calc 35 | 2 * (b + 1) = 2 * b + 2 * 1 : by rw mul_add 36 | ... = 2 * b + 1 + 1 : by rw two_times_one 37 | ... = a + 1 : by rw hb 38 | ) 39 | ) 40 | 41 | def eoro (a : ℕ) := (is_even a) ∨ (is_odd a) 42 | 43 | lemma even_plus_one_eoro (a : ℕ) (h: is_even a) : eoro (a + 1) := 44 | (or.intro_right (is_even (a + 1)) (even_plus_one_is_odd a h)) 45 | 46 | lemma odd_plus_one_eoro (a : ℕ) (h: is_odd a) : eoro (a + 1) := 47 | (or.intro_left (is_odd (a + 1)) (odd_plus_one_is_even a h)) 48 | 49 | lemma eoro_inducts (a : ℕ) (h: eoro a) : eoro (a + 1) := 50 | or.elim h 51 | (assume he: is_even a, show eoro (a + 1), from even_plus_one_eoro a he) 52 | (assume ho: is_odd a, show eoro (a + 1), from odd_plus_one_eoro a ho) 53 | 54 | lemma zero_eoro : (eoro 0) := or.intro_left (is_odd 0) zero_is_even 55 | 56 | theorem all_eoro (n : ℕ) : eoro n := 57 | nat.rec_on n 58 | (show eoro 0, from zero_eoro) 59 | (assume n, 60 | assume hn : eoro n, 61 | show eoro (n + 1), 62 | from eoro_inducts n hn) 63 | 64 | def times_successor (n : ℕ) := n * (n + 1) 65 | 66 | theorem even_times_any (a b : ℕ) (h: is_even a) : is_even (a * b) := 67 | exists.elim h 68 | (assume c, assume hc: 2 * c = a, show is_even (a * b), from 69 | exists.intro (c * b) 70 | (calc 71 | 2 * (c * b) = (2 * c) * b : by rw mul_assoc 2 c b 72 | ... = a * b : by rw hc)) 73 | 74 | theorem any_times_even (a b : ℕ) (h: is_even b): is_even (a * b) := 75 | have h1: b * a = a * b, from (mul_comm b a), 76 | have h2: is_even (b * a), from (even_times_any b a h), 77 | h1 ▸ h2 78 | 79 | lemma even_tse (a : ℕ) (h: is_even a) : is_even (times_successor a) := 80 | even_times_any a (a + 1) h 81 | 82 | lemma odd_tse (a : ℕ) (h1: is_odd a) : is_even (times_successor a) := 83 | have h2: is_even (a + 1), from (odd_plus_one_is_even a h1), 84 | any_times_even a (a + 1) h2 85 | 86 | theorem times_successor_even (a : ℕ) : is_even (times_successor a) := 87 | have h: eoro a, from all_eoro a, 88 | or.elim h 89 | (assume he: is_even a, show is_even (times_successor a), from even_tse a he) 90 | (assume ho: is_odd a, show is_even (times_successor a), from odd_tse a ho) 91 | 92 | def is_composite (a : ℕ) := ∃ b, ∃ c, b > 1 ∧ c > 1 ∧ b * c = a 93 | 94 | theorem composite_divisor_lt (a b c : ℕ) (h1: a * b = c) (h2: a > 1) (h3: b > 1) : a < c := 95 | have h4: 0 < b, from nat.lt_of_succ_lt h3, 96 | have h5: a ≤ a * b, from nat.le_mul_of_pos_right h4, 97 | have h6: a ≤ c, from eq.subst h1 h5, 98 | have h7: a = c ∨ a ≠ c, from em(a=c), 99 | or.elim h7 100 | (assume h8: a = c, 101 | have h9: a * b = a, from (rfl.congr (eq.symm h8)).mp h1, 102 | have h10: 0 < a, from nat.lt_of_succ_lt h2, 103 | have h11: b = 1, from (nat.mul_right_eq_self_iff h10).mp h9, 104 | have h12: b ≠ 1, from ne_of_gt h3, 105 | absurd h11 h12) 106 | (assume : a ≠ c, 107 | show a < c, from lt_of_le_of_ne h6 this) 108 | 109 | def is_prime (p : ℕ) := p > 1 ∧ ¬ (is_composite p) 110 | 111 | theorem prime_pos (p : ℕ) (h1: is_prime p) : p > 0 := 112 | have p > 1, from h1.left, 113 | show p > 0, from nat.lt_of_succ_lt this 114 | 115 | def divides (a b : ℕ) := ∃ c, a * c = b 116 | 117 | def is_empty (s : set ℕ) := ∀ a : ℕ, a ∉ s 118 | def lower_bound (a : ℕ) (s : set ℕ) := ∀ b : ℕ, b ∈ s → a ≤ b 119 | def upper_bound (a : ℕ) (s : set ℕ) := ∀ b : ℕ, b ∈ s → a ≥ b 120 | def is_smallest (a : ℕ) (s : set ℕ) := a ∈ s ∧ lower_bound a s 121 | def is_largest (a : ℕ) (s : set ℕ) := a ∈ s ∧ upper_bound a s 122 | 123 | theorem not_ltz (a : ℕ) : ¬ (a < 0) := not_lt_bot 124 | 125 | lemma lower_bound_union (s1 s2 : set ℕ) (a1 a2 : ℕ) 126 | (h1: lower_bound a1 s1) (h2: lower_bound a2 s2) (h3 : a1 ≤ a2) : 127 | lower_bound a1 (s1 ∪ s2) := 128 | assume b : ℕ, 129 | assume h4: b ∈ (s1 ∪ s2), 130 | or.elim h4 131 | (assume h5: b ∈ s1, show a1 ≤ b, from h1 b h5) 132 | (assume h6: b ∈ s2, 133 | have h7: a2 ≤ b, from h2 b h6, 134 | show a1 ≤ b, from le_trans h3 h7) 135 | 136 | lemma upper_bound_union (s1 s2 : set ℕ) (a1 a2 : ℕ) 137 | (h1: upper_bound a1 s1) (h2: upper_bound a2 s2) (h3 : a1 ≤ a2) : 138 | upper_bound a2 (s1 ∪ s2) := 139 | assume b : ℕ, 140 | assume h4: b ∈ s1 ∪ s2, 141 | or.elim h4 142 | (assume h5: b ∈ s1, 143 | have h6: a1 ≥ b, from h1 b h5, 144 | show a2 ≥ b, from le_trans h6 h3) 145 | (assume h7: b ∈ s2, 146 | show a2 ≥ b, from h2 b h7) 147 | 148 | lemma is_smallest_union (s1 s2 : set ℕ) (a1 a2 : ℕ) 149 | (h1 : is_smallest a1 s1) (h2 : is_smallest a2 s2) (h3 : a1 ≤ a2) : 150 | is_smallest a1 (s1 ∪ s2) := 151 | have h4: a1 ∈ (s1 ∪ s2), from set.mem_union_left s2 h1.left, 152 | have h5: lower_bound a1 (s1 ∪ s2), from lower_bound_union s1 s2 a1 a2 h1.right h2.right h3, 153 | and.intro h4 h5 154 | 155 | lemma inne (s : set ℕ) (a b : ℕ) (ha : a ∈ s) (hb : b ∉ s) : a ≠ b := 156 | have h1: a = b ∨ a ≠ b, from em(a=b), 157 | or.elim h1 158 | (assume h2: a = b, 159 | have h3: b ∈ s, from eq.subst h2 ha, 160 | show a ≠ b, from absurd h3 hb) 161 | (assume h4: a ≠ b, show a ≠ b, from h4) 162 | 163 | lemma lbih (s : set ℕ) (n : ℕ) (h1 : lower_bound n s) (h2 : n ∉ s) : lower_bound (n+1) s := 164 | assume b : ℕ, 165 | assume h3 : b ∈ s, 166 | have h4: b ≠ n, from inne s b n h3 h2, 167 | have h5: b ≥ n, from h1 b h3, 168 | have h6: b > n, from lt_of_le_of_ne h5 (ne.symm h4), 169 | show b ≥ n + 1, from h6 170 | 171 | lemma lbi1 (s : set ℕ) (n : ℕ) (h : lower_bound n s) : is_smallest n s ∨ lower_bound (n+1) s := 172 | have h1: n ∈ s ∨ n ∉ s, from em(n ∈ s), 173 | or.elim h1 174 | (assume h2: n ∈ s, 175 | have h3: is_smallest n s, from and.intro h2 h, 176 | or.inl h3) 177 | (assume h3: n ∉ s, 178 | have h4: lower_bound (n+1) s, from lbih s n h h3, 179 | or.inr h4) 180 | 181 | lemma lbiz (s : set ℕ) : lower_bound 0 s := 182 | assume b : ℕ, 183 | assume h: b ∈ s, 184 | show b ≥ 0, from bot_le 185 | 186 | lemma lbi2 (s : set ℕ) (n : ℕ) : lower_bound n s ∨ ∃ a, is_smallest a s := 187 | nat.rec_on n 188 | (or.inl (lbiz s)) 189 | (assume n, 190 | assume h1: lower_bound n s ∨ ∃ a, is_smallest a s, 191 | or.elim h1 192 | (assume h2: lower_bound n s, 193 | have h3: is_smallest n s ∨ lower_bound (n+1) s, from lbi1 s n h2, 194 | or.elim h3 195 | (assume h4: is_smallest n s, 196 | have h5: ∃ a, is_smallest a s, from exists.intro n h4, 197 | or.inr h5) 198 | (assume h6: lower_bound (n+1) s, 199 | or.inl h6) 200 | ) 201 | (assume ha: ∃ a, is_smallest a s, 202 | or.inr ha) 203 | ) 204 | 205 | lemma nlb (s : set ℕ) (n : ℕ) (h1: n ∈ s) (h2: lower_bound (n+1) s) : false := 206 | have h3: n + 1 ≤ n, from h2 n h1, 207 | have h4: n + 1 > n, from lt_add_one n, 208 | show false, from nat.lt_le_antisymm h4 h3 209 | 210 | theorem well_ordered (s : set ℕ) (h1: s.nonempty) : ∃ a, is_smallest a s := 211 | exists.elim h1 212 | (assume n, 213 | assume h2: n ∈ s, 214 | have h3: lower_bound (n+1) s ∨ ∃ a, is_smallest a s, from lbi2 s (n+1), 215 | or.elim h3 216 | (assume h4: lower_bound (n+1) s, 217 | have h5: false, from nlb s n h2 h4, 218 | false.rec (∃ a, is_smallest a s) h5) 219 | (assume h6: ∃ a, is_smallest a s, show ∃ a, is_smallest a s, from h6)) 220 | 221 | theorem one_divides (n : ℕ) : divides 1 n := 222 | have h: 1 * n = n, from one_mul n, 223 | exists.intro n h 224 | 225 | theorem divides_zero (n : ℕ) : divides n 0 := 226 | have h: n * 0 = 0, from rfl, 227 | exists.intro 0 h 228 | 229 | theorem divides_nonzero (a b : ℕ) (h1: ¬ divides a b) : b ≠ 0 := 230 | have h2: b = 0 ∨ b ≠ 0, from (em(b = 0)), 231 | or.elim h2 232 | (assume h3: b = 0, 233 | have h4: divides a 0, from divides_zero a, 234 | have h5: divides a b, from eq.subst (eq.symm h3) h4, 235 | show b ≠ 0, from absurd h5 h1) 236 | (assume h6: b ≠ 0, show b ≠ 0, from h6) 237 | 238 | def divisors (n : ℕ) := { d : ℕ | divides d n } 239 | 240 | theorem divisors_nonempty (n : ℕ) : (divisors n).nonempty := 241 | have 1 ∈ (divisors n), from one_divides n, 242 | show (divisors n).nonempty, from set.nonempty_of_mem this 243 | 244 | lemma rdivisor_nonzero (a b c : ℕ) (h1 : c > 0) (h2 : a * b = c) : b > 0 := 245 | have h3: b = 0 ∨ b > 0, from nat.eq_zero_or_pos b, 246 | or.elim h3 247 | (assume h4: b = 0, 248 | have h5: a * 0 = 0, from rfl, 249 | have h6: a * b = 0, from eq.subst (eq.symm h4) h5, 250 | have h7: c = 0, from eq.subst h2 h6, 251 | have h8: ¬ (0 > 0), from irrefl 0, 252 | have h9: ¬ (c > 0), from eq.subst (eq.symm h7) h8, 253 | absurd h1 h9) 254 | (assume h10: b > 0, 255 | h10) 256 | 257 | theorem divisors_nonzero (a b c : ℕ) (h1 : c > 0) (h2 : a * b = c) : a > 0 ∧ b > 0 := 258 | have h3: b > 0, from rdivisor_nonzero a b c h1 h2, 259 | have h4: b * a = c, from eq.subst (mul_comm a b) h2, 260 | have h5: a > 0, from rdivisor_nonzero b a c h1 h4, 261 | and.intro h5 h3 262 | 263 | theorem prime_divisors (d p : ℕ) (h1: is_prime p) (h2: divides d p) : d = 1 ∨ d = p := 264 | have h3: (d = 1 ∨ d = p) ∨ ¬ (d = 1 ∨ d = p), from em(d = 1 ∨ d = p), 265 | or.elim h3 266 | (assume h4: d = 1 ∨ d = p, 267 | h4) 268 | (assume h5: ¬ (d = 1 ∨ d = p), 269 | have h6: ¬ (d = 1) ∧ ¬ (d = p), from or_imp_distrib.mp h5, 270 | have h7: d ≠ 1, from h6.left, 271 | have h8: d ≠ p, from h6.right, 272 | have h9: p > 1, from h1.left, 273 | exists.elim h2 274 | (assume c, 275 | assume h10: d * c = p, 276 | have h11: p > 0, from nat.lt_of_succ_lt h9, 277 | have h12: d > 0 ∧ c > 0, from divisors_nonzero d c p h11 h10, 278 | have h13: d > 0, from h12.left, 279 | have h14: d > 1, from lt_of_le_of_ne h13 (ne.symm h7), 280 | have h15: c > 0, from h12.right, 281 | have h16: c = 1 ∨ c ≠ 1, from em(c = 1), 282 | or.elim h16 283 | (assume h17: c = 1, 284 | have h18: d * 1 = p, from eq.subst h17 h10, 285 | have h19: d = p, from eq.subst (mul_one d) h18, 286 | absurd h19 h8) 287 | (assume h20: c ≠ 1, 288 | have h21: c > 1, from lt_of_le_of_ne h15 (ne.symm h20), 289 | have h22: d > 1 ∧ c > 1 ∧ d * c = p, from and.intro h14 (and.intro h21 h10), 290 | have h23: is_composite p, from exists.intro d (exists.intro c h22), 291 | absurd h23 h1.right))) 292 | 293 | theorem divisors_bounded (n : ℕ) (h : n > 0) : upper_bound n (divisors n) := 294 | assume a, 295 | assume h1: a ∈ divisors n, 296 | have h2: divides a n, from h1, 297 | have h3: 0 < n, from h, 298 | have h4: ∃ c, a * c = n, from h1, 299 | exists.elim h4 300 | (assume b, 301 | assume h5: a * b = n, 302 | have h6: b > 0, from rdivisor_nonzero a b n h h5, 303 | have h7: a ≤ a * b, from nat.le_mul_of_pos_right h6, 304 | show a ≤ n, from eq.subst h5 h7) 305 | 306 | def flip_set (s : set ℕ) (n : ℕ) := { a : ℕ | a ≤ n ∧ n - a ∈ s } 307 | 308 | lemma df1 (s : set ℕ) (n : ℕ) : flip_set (flip_set s n) n ⊆ s := 309 | assume a, 310 | assume h1: a ∈ flip_set (flip_set s n) n, 311 | have h2: a ≤ n, from h1.left, 312 | have h3: n - a ∈ flip_set s n, from h1.right, 313 | have h4: n - (n - a) ∈ s, from h3.right, 314 | have h5: n - (n - a) = a, from nat.sub_sub_self h2, 315 | show a ∈ s, from eq.subst h5 h4 316 | 317 | lemma df2 (s : set ℕ) (n : ℕ) (h1 : upper_bound n s) : s ⊆ flip_set (flip_set s n) n := 318 | assume a, 319 | assume h2: a ∈ s, 320 | have h3: n - a ≤ n, from nat.sub_le n a, 321 | have h4: a ≤ n, from h1 a h2, 322 | have h5: n - (n - a) = a, from nat.sub_sub_self h4, 323 | have h6: n - (n - a) ∈ s, from eq.subst (eq.symm h5) h2, 324 | have h7: n - a ∈ flip_set s n, from and.intro h3 h6, 325 | show a ∈ flip_set (flip_set s n) n, from and.intro h4 h7 326 | 327 | def double_flip (s : set ℕ) (n : ℕ) (h1: upper_bound n s) : flip_set (flip_set s n) n = s := 328 | set.subset.antisymm (df1 s n) (df2 s n h1) 329 | 330 | lemma lb_flips (s : set ℕ) (a n : ℕ) (h1: lower_bound a s) : 331 | upper_bound (n - a) (flip_set s n) := 332 | (assume b, 333 | assume h2: b ∈ (flip_set s n), 334 | have h3: n - b ∈ s, from h2.right, 335 | have h4: a ≤ n - b, from h1 (n-b) h3, 336 | have h5: b ≤ n, from h2.left, 337 | have h6: a + b ≤ n, from (nat.add_le_to_le_sub a h5).mpr h4, 338 | show n - a ≥ b, from nat.le_sub_left_of_add_le h6) 339 | 340 | lemma smallest_flips (s : set ℕ) (a n : ℕ) (h1: upper_bound n s) (h2: is_smallest a s) : 341 | is_largest (n-a) (flip_set s n) := 342 | have h3: a ∈ s, from h2.left, 343 | have h4: n ≥ a, from h1 a h3, 344 | have h5: n - (n - a) = a, from nat.sub_sub_self h4, 345 | have h6: n - (n - a) ∈ s, from set.mem_of_eq_of_mem h5 h3, 346 | have h7: n - a ≤ n, from nat.sub_le n a, 347 | have h8: n - a ∈ (flip_set s n), from and.intro h7 h6, 348 | have h9: upper_bound (n - a) (flip_set s n), from lb_flips s a n h2.right, 349 | and.intro h8 h9 350 | 351 | lemma nonempty_flips (s : set ℕ) (n : ℕ) (h1: upper_bound n s) (h2: s.nonempty) : 352 | (flip_set s n).nonempty := 353 | exists.elim h2 354 | (assume a, 355 | assume h3: a ∈ s, 356 | have h4: a ≤ n, from h1 a h3, 357 | have h5: n - (n - a) = a, from nat.sub_sub_self h4, 358 | have h6: n - a ≤ n, from nat.sub_le n a, 359 | have h7: n - (n - a) ∈ s, from eq.subst (eq.symm h5) h3, 360 | have h6: n - a ∈ flip_set s n, from set.mem_sep h6 h7, 361 | show (flip_set s n).nonempty, from set.nonempty_of_mem h6) 362 | 363 | theorem bounded_has_largest (s : set ℕ) (n : ℕ) (h1: upper_bound n s) (h2: s.nonempty) : 364 | ∃ a, is_largest a s := 365 | have h3: (flip_set s n).nonempty, from nonempty_flips s n h1 h2, 366 | have h4: ∃ x, is_smallest x (flip_set s n), from well_ordered (flip_set s n) h3, 367 | exists.elim h4 368 | (assume b, 369 | assume h5: is_smallest b (flip_set s n), 370 | have h6: upper_bound n (flip_set s n), from lb_flips s 0 n (lbiz s), 371 | have h7: is_largest (n-b) (flip_set (flip_set s n) n), 372 | from (smallest_flips (flip_set s n) b n h6 h5), 373 | have h8: flip_set (flip_set s n) n = s, from double_flip s n h1, 374 | have h9: is_largest (n-b) s, from eq.subst h8 h7, 375 | show ∃ a, is_largest a s, from exists.intro (n-b) h9) 376 | 377 | def common_divisors (a b : ℕ) := (divisors a) ∩ (divisors b) 378 | 379 | def is_gcd (d a b : ℕ) := is_largest d (common_divisors a b) 380 | 381 | def relatively_prime (a b : ℕ) := is_gcd 1 a b 382 | 383 | theorem division (a m : ℕ) (h1: m > 0) : ∃ c : ℕ, ∃ d : ℕ, m * c + d = a ∧ d < m := 384 | nat.rec_on a 385 | (have h2: m * 0 + 0 = 0, from rfl, 386 | have h3: m * 0 + 0 = 0 ∧ 0 < m, from and.intro h2 h1, 387 | have h4: ∃ d : ℕ, m * 0 + d = 0 ∧ d < m, from exists.intro 0 h3, 388 | show ∃ c : ℕ, ∃ d : ℕ, m * c + d = 0 ∧ d < m, from exists.intro 0 h4) 389 | (assume n, 390 | assume h5: ∃ c : ℕ, ∃ d : ℕ, m * c + d = n ∧ d < m, 391 | exists.elim h5 392 | (assume c, 393 | assume h6: ∃ d : ℕ, m * c + d = n ∧ d < m, 394 | exists.elim h6 395 | (assume d, 396 | assume h7: m * c + d = n ∧ d < m, 397 | have h8: d + 1 = m ∨ d + 1 ≠ m, from em(d + 1 = m), 398 | have h9: m * c + d = n, from h7.left, 399 | have h10: m * c + d + 1 = n + 1, from congr_fun (congr_arg has_add.add h9) 1, 400 | or.elim h8 401 | (assume h11: d + 1 = m, 402 | have h12: m * (c + 1) + 0 = n + 1, from (congr_arg (has_add.add (m * c)) (eq.symm h11)).trans h10, 403 | have h13: ∃ f : ℕ, m * (c + 1) + f = n + 1 ∧ f < m, from exists.intro 0 (and.intro h12 h1), 404 | show ∃ e : ℕ, ∃ f : ℕ, m * e + f = n + 1 ∧ f < m, from exists.intro (c+1) h13) 405 | (assume h14: d + 1 ≠ m, 406 | have h15: d < m, from h7.right, 407 | have h16: d + 1 < m, from lt_of_le_of_ne h15 h14, 408 | have h17: ∃ f : ℕ, m * c + f = n + 1 ∧ f < m, from exists.intro (d+1) (and.intro h10 h16), 409 | show ∃ e : ℕ, ∃ f : ℕ, m * e + f = n + 1 ∧ f < m, from exists.intro c h17) 410 | ))) 411 | 412 | theorem divides_self (n : ℕ) : divides n n := 413 | have h1: n * 1 = n, from mul_one n, 414 | exists.intro 1 h1 415 | 416 | theorem divides_add (d a b : ℕ) (h1: divides d a) (h2: divides d b) : divides d (a + b) := 417 | exists.elim h1 418 | (assume e, 419 | assume h3: d * e = a, 420 | exists.elim h2 421 | (assume f, 422 | assume h4: d * f = b, 423 | have h5: d * (e + f) = d * e + d * f, from mul_add d e f, 424 | have h6: d * (e + f) = a + d * f, from eq.subst h3 h5, 425 | have h7: d * (e + f) = a + b, from eq.subst h4 h6, 426 | show divides d (a + b), from exists.intro (e + f) h7)) 427 | 428 | theorem divides_sub (d a b : ℕ) (h1: divides d a) (h2: divides d b) : divides d (a - b) := 429 | exists.elim h1 430 | (assume e, 431 | assume h3: d * e = a, 432 | exists.elim h2 433 | (assume f, 434 | assume h4: d * f = b, 435 | have h5: d * (e - f) = d * e - d * f, from nat.mul_sub_left_distrib d e f, 436 | have h6: d * (e - f) = a - d * f, from eq.subst h3 h5, 437 | have h7: d * (e - f) = a - b, from eq.subst h4 h6, 438 | show divides d (a - b), from exists.intro (e - f) h7)) 439 | 440 | theorem divides_mul (d a b : ℕ) (h1: divides d a) : divides d (a * b) := 441 | exists.elim h1 442 | (assume c, 443 | assume h2: d * c = a, 444 | have h3: d * c * b = a * b, from congr_fun (congr_arg has_mul.mul h2) b, 445 | have h4: d * (c * b) = a * b, from eq.subst (mul_assoc d c b) h3, 446 | exists.intro (c*b) h4) 447 | 448 | theorem divides_trans (a b c : ℕ) (h1: divides a b) (h2: divides b c) : divides a c := 449 | exists.elim h2 450 | (assume e, 451 | assume h3: b * e = c, 452 | have h4: divides a (b * e), from divides_mul a b e h1, 453 | show divides a c, from eq.subst h3 h4) 454 | 455 | theorem divides_le (a b : ℕ) (h1: divides a b) (h2: b > 0) : a ≤ b := 456 | exists.elim h1 457 | (assume c, 458 | assume h3: a * c = b, 459 | have h4: c = 0 ∨ c ≠ 0, from em(c = 0), 460 | or.elim h4 461 | (assume h5: c = 0, 462 | have h6: a * 0 = 0, from rfl, 463 | have h7: a * c = 0, from eq.subst h5.symm h6, 464 | have h8: b = 0, from eq.subst h3 h7, 465 | have h9: b ≠ 0, from ne_of_gt h2, 466 | absurd h8 h9) 467 | (assume h10: c ≠ 0, 468 | have h11: 0 < c, from bot_lt_iff_ne_bot.mpr h10, 469 | have h12: a ≤ a * c, from nat.le_mul_of_pos_right h11, 470 | eq.subst h3 h12)) 471 | 472 | def eset (p b : ℕ) (h: is_prime p) := { x : ℕ | x > 0 ∧ divides p (x*b) } 473 | 474 | theorem eset_nonempty (p b : ℕ) (h1: is_prime p) : (eset p b h1).nonempty := 475 | have h2: p > 0, from prime_pos p h1, 476 | have h3: divides p (p*b), from exists.intro b rfl, 477 | have h4: p ∈ (eset p b h1), from and.intro h2 h3, 478 | show (eset p b h1).nonempty, from set.nonempty_of_mem h4 479 | 480 | lemma ehelp (a b p x0 x : ℕ) (h1: is_prime p) (h2: is_smallest x0 (eset p b h1)) (h3: x ∈ eset p b h1) : 481 | divides x0 x := 482 | have h4: x0 > 0, from h2.left.left, 483 | have h5: ∃ q : ℕ, ∃ r : ℕ, x0 * q + r = x ∧ r < x0, from division x x0 h4, 484 | exists.elim h5 485 | (assume q, 486 | assume h6: ∃ r : ℕ, x0 * q + r = x ∧ r < x0, 487 | exists.elim h6 488 | (assume r, 489 | assume h7: x0 * q + r = x ∧ r < x0, 490 | have h8: x - x0 * q = r, from nat.sub_eq_of_eq_add (eq.symm h7.left), 491 | have h9: (x - x0 * q) * b = r * b, from congr_fun (congr_arg has_mul.mul h8) b, 492 | have h10: x * b - x0 * q * b = r * b, from eq.subst (nat.mul_sub_right_distrib x (x0*q) b) h9, 493 | have h11: divides p (x * b), from h3.right, 494 | have h12: divides p (x0 * b), from h2.left.right, 495 | have h13: divides p (x0 * b * q), from divides_mul p (x0 * b) q h12, 496 | have h14: divides p (x0 * (b * q)), from eq.subst (mul_assoc x0 b q) h13, 497 | have h15: divides p (x0 * (q * b)), from eq.subst (mul_comm b q) h14, 498 | have h16: divides p (x0 * q * b), from eq.subst (eq.symm (mul_assoc x0 q b)) h15, 499 | have h17: divides p (x * b - x0 * q * b), from divides_sub p (x * b) (x0 * q * b) h11 h16, 500 | have h18: divides p (r * b), from eq.subst h10 h17, 501 | have h19: r = 0 ∨ r ≠ 0, from em(r=0), 502 | or.elim h19 503 | (assume h20: r = 0, 504 | have h21: x0 * q + 0 = x, from eq.subst h20 h7.left, 505 | have h22: x0 * q = x, from h21, 506 | show divides x0 x, from exists.intro q h22) 507 | (assume h23: r ≠ 0, 508 | have h24: r > 0, from bot_lt_iff_ne_bot.mpr h23, 509 | have h25: r ∈ eset p b h1, from and.intro h24 h18, 510 | have h26: r < x0, from h7.right, 511 | have h27: lower_bound x0 (eset p b h1), from h2.right, 512 | have h28: x0 ≤ r, from h27 r h25, 513 | have h29: ¬ (r < x0), from not_lt.mpr h28, 514 | show divides x0 x, from absurd h26 h29))) 515 | 516 | theorem euclids_lemma (p a b : ℕ) (h1 : is_prime p) (h2 : divides p (a * b)) 517 | : divides p a ∨ divides p b := 518 | have h3: divides p a ∨ ¬ divides p a, from em(divides p a), 519 | or.elim h3 520 | (assume h4: divides p a, or.inl h4) 521 | (assume h5: ¬ divides p a, 522 | have h6: (eset p b h1).nonempty, from eset_nonempty p b h1, 523 | have h7: ∃ x0, is_smallest x0 (eset p b h1), from well_ordered (eset p b h1) h6, 524 | exists.elim h7 525 | (assume x0, 526 | assume h8: is_smallest x0 (eset p b h1), 527 | have h9: divides p (p * b), from exists.intro b rfl, 528 | have h10: p ∈ eset p b h1, from and.intro (prime_pos p h1) h9, 529 | have h11: a ≠ 0, from divides_nonzero p a h5, 530 | have h12: a > 0, from bot_lt_iff_ne_bot.mpr h11, 531 | have h13: a ∈ eset p b h1, from and.intro h12 h2, 532 | have h14: divides x0 p, from ehelp a b p x0 p h1 h8 h10, 533 | have h15: divides x0 a, from ehelp a b p x0 a h1 h8 h13, 534 | have h16: x0 = 1 ∨ x0 = p, from prime_divisors x0 p h1 h14, 535 | or.elim h16 536 | (assume h17: x0 = 1, 537 | have h18: divides p (x0 * b), from h8.left.right, 538 | have h19: 1 * b = b, from one_mul b, 539 | have h20: x0 * b = b, from eq.subst (eq.symm h17) h19, 540 | have h21: divides p b, from eq.subst h20 h18, 541 | or.inr h21) 542 | (assume h22: x0 = p, 543 | have h23: divides p a, from eq.subst h22 h15, 544 | absurd h23 h5))) 545 | 546 | def g1_divisors (n : ℕ) := { d : ℕ | d > 1 ∧ divides d n } 547 | 548 | theorem smallest_g1_divisor_prime (p n : ℕ) (h1: is_smallest p (g1_divisors n)) : is_prime p := 549 | have h2: is_composite p ∨ ¬ is_composite p, from em(is_composite p), 550 | have h3: p > 1, from h1.left.left, 551 | or.elim h2 552 | (assume h3: is_composite p, 553 | exists.elim h3 554 | (assume b, 555 | assume h4: ∃ c, b > 1 ∧ c > 1 ∧ b * c = p, 556 | exists.elim h4 557 | (assume c, 558 | assume h5: b > 1 ∧ c > 1 ∧ b * c = p, 559 | have h6: divides b p, from exists.intro c h5.right.right, 560 | have h7: divides b n, from divides_trans b p n h6 h1.left.right, 561 | have h8: b ∈ g1_divisors n, from and.intro h5.left h7, 562 | have h9: b < p, from composite_divisor_lt b c p h5.right.right h5.left h5.right.left, 563 | have h10: p ≤ b, from h1.right b h8, 564 | have h11: ¬ (b < p), from not_lt.mpr h10, 565 | absurd h9 h11))) 566 | (assume hz: ¬ is_composite p, and.intro h3 hz) 567 | 568 | theorem g1_divisors_nonempty (n : ℕ) (h1: n > 1) : (g1_divisors n).nonempty := 569 | have h2: divides n n, from divides_self n, 570 | have h3: n ∈ g1_divisors n, from and.intro h1 h2, 571 | set.nonempty_of_mem h3 572 | 573 | theorem has_prime_divisor (n : ℕ) (h1: n > 1) : ∃ p, is_prime p ∧ divides p n := 574 | have h2: ∃ p, is_smallest p (g1_divisors n), 575 | from well_ordered (g1_divisors n) (g1_divisors_nonempty n h1), 576 | exists.elim h2 577 | (assume p, 578 | assume h2: is_smallest p (g1_divisors n), 579 | have h3: is_prime p, from smallest_g1_divisor_prime p n h2, 580 | have h4: divides p n, from h2.left.right, 581 | exists.intro p (and.intro h3 h4)) 582 | 583 | def codivisors (a b : ℕ) := {d : ℕ | divides d a ∧ divides d b} 584 | 585 | theorem codivisors_comm (a b: ℕ) : codivisors a b = codivisors b a := 586 | have h1: codivisors a b = divisors a ∩ divisors b, from rfl, 587 | have h2: divisors a ∩ divisors b = divisors b ∩ divisors a, 588 | from set.inter_comm (divisors a) (divisors b), 589 | have h3: codivisors b a = divisors b ∩ divisors a, from rfl, 590 | by rw [h1, h2, h3.symm] 591 | 592 | def coprime (a b : ℕ) := upper_bound 1 (codivisors a b) 593 | 594 | theorem coprime_comm (a b: ℕ) (h1: coprime a b) : coprime b a := 595 | have h2: upper_bound 1 (codivisors a b), from h1, 596 | have h3: upper_bound 1 (codivisors b a), from eq.subst (codivisors_comm a b) h2, 597 | h3 598 | 599 | lemma not_coprime (x y : ℕ) (h1: ¬ coprime x y) : ∃ z, z > 1 ∧ divides z x ∧ divides z y := 600 | have h2: ∃ b : ℕ, ¬ (b ∈ codivisors x y → 1 ≥ b), from classical.not_forall.mp h1, 601 | exists.elim h2 602 | (assume b, 603 | assume h4: ¬ (b ∈ codivisors x y → 1 ≥ b), 604 | have h5: b ∈ codivisors x y ∧ ¬ (1 ≥ b), from classical.not_imp.mp h4, 605 | have h6: divides b x ∧ divides b y, from h5.left, 606 | have h7: ¬ (1 ≥ b), from h5.right, 607 | have h8: b > 1, from not_le.mp h7, 608 | have h9: b > 1 ∧ divides b x ∧ divides b y, from and.intro h8 h6, 609 | exists.intro b h9) 610 | 611 | lemma div_not_coprime (p a b: ℕ) (h1: is_prime p) (h2: divides p a) (h3: divides p b) : 612 | ¬ coprime a b := 613 | have h4: p > 1, from h1.left, 614 | have h5: p ∈ codivisors a b, from set.mem_sep h2 h3, 615 | have h6: coprime a b ∨ ¬ coprime a b, from em(coprime a b), 616 | or.elim h6 617 | (assume h7: coprime a b, 618 | have h8: 1 ≥ p, from h7 p h5, 619 | have h9: ¬ (p > 1), from not_lt.mpr h8, 620 | absurd h4 h9) 621 | (assume: ¬ coprime a b, this) 622 | 623 | theorem single_cofactor (a b : ℕ) (h1: a > 0) (h2: b > 0) : 624 | coprime a b ∨ ∃ p, is_prime p ∧ divides p a ∧ divides p b := 625 | have h3: coprime a b ∨ ¬ coprime a b, from em(coprime a b), 626 | or.elim h3 627 | (assume h4: coprime a b, 628 | or.inl h4) 629 | (assume h5: ¬ coprime a b, 630 | have h6: ∃ d : ℕ, d > 1 ∧ divides d a ∧ divides d b, from not_coprime a b h5, 631 | exists.elim h6 632 | (assume d, 633 | assume h7: d > 1 ∧ divides d a ∧ divides d b, 634 | have h8: ∃ p, is_prime p ∧ divides p d, from has_prime_divisor d h7.left, 635 | exists.elim h8 636 | (assume p, 637 | assume h9: is_prime p ∧ divides p d, 638 | have h10: divides p a, from divides_trans p d a h9.right h7.right.left, 639 | have h11: divides p b, from divides_trans p d b h9.right h7.right.right, 640 | or.inr (exists.intro p (and.intro h9.left (and.intro h10 h11)))))) 641 | 642 | theorem coprime_mult (a b c: ℕ) (h1: a > 0) (h2: b > 0) (h3: c > 0) 643 | (h4: coprime a b) (h5: coprime a c) : 644 | coprime a (b*c) := 645 | have h6: b*c > 0, from mul_pos h2 h3, 646 | have h7: coprime a (b*c) ∨ ∃ p, is_prime p ∧ divides p a ∧ divides p (b*c), 647 | from single_cofactor a (b*c) h1 h6, 648 | or.elim h7 649 | (assume: coprime a (b*c), this) 650 | (assume h8: ∃ p, is_prime p ∧ divides p a ∧ divides p (b*c), 651 | exists.elim h8 652 | (assume p, 653 | assume h9: is_prime p ∧ divides p a ∧ divides p (b*c), 654 | have h10: divides p b ∨ divides p c, from euclids_lemma p b c h9.left h9.right.right, 655 | or.elim h10 656 | (assume h11: divides p b, 657 | have h12: ¬ coprime a b, from div_not_coprime p a b h9.left h9.right.left h11, 658 | absurd h4 h12) 659 | (assume h13: divides p c, 660 | have h14: ¬ coprime a c, from div_not_coprime p a c h9.left h9.right.left h13, 661 | absurd h5 h14))) 662 | 663 | theorem coprime_nonzero (a b: ℕ) (h1: a > 1) (h2: coprime a b) : b > 0 := 664 | have h3: b = 0 ∨ b ≠ 0, from em(b = 0), 665 | or.elim h3 666 | (assume h4: b = 0, 667 | have h5: divides a 0, from divides_zero a, 668 | have h6: divides a b, from eq.subst h4.symm h5, 669 | have h7: divides a a, from divides_self a, 670 | have h8: a ∈ codivisors a b, from set.mem_sep h7 h6, 671 | have h9: 1 ≥ a, from h2 a h8, 672 | have h10: ¬ (a > 1), from not_lt.mpr h9, 673 | absurd h1 h10) 674 | (assume h11: b ≠ 0, 675 | bot_lt_iff_ne_bot.mpr h11) 676 | 677 | def linear_combo (a b : ℕ) := { e : ℕ | ∃ c : ℕ, ∃ d : ℕ, a * c = b * d + e } 678 | 679 | theorem lc_left (a b : ℕ) : a ∈ linear_combo a b := 680 | have h1: a * 1 = b * 0 + a, from rfl, 681 | exists.intro 1 (exists.intro 0 h1) 682 | 683 | theorem lc_right (a b : ℕ) (h1: a > 0) : b ∈ linear_combo a b := 684 | have h2: b*(a-1) + b = b * (a-1) + b, from rfl, 685 | have h3: b*(a-1) = b*a - b, from nat.mul_pred_right b (nat.sub a 0), 686 | have h4: b*a - b + b = b * (a-1) + b, from eq.subst h3 h2, 687 | have h5: b ≤ b*a, from nat.le_mul_of_pos_right h1, 688 | have h6: b*a - b + b = b*a + b - b, from nat.sub_add_eq_add_sub h5, 689 | have h7: b*a + b - b = b*a, from (b*a).add_sub_cancel b, 690 | have h8: a * b = b * a, from mul_comm a b, 691 | have h9: a * b = b * (a-1) + b, by rw [h8, h7.symm, h6.symm, h4], 692 | exists.intro b (exists.intro (a-1) h9) 693 | 694 | theorem lc_zero (a b : ℕ) : 0 ∈ linear_combo a b := 695 | have h1: a * 0 = b * 0 + 0, from rfl, 696 | exists.intro 0 (exists.intro 0 h1) 697 | 698 | theorem lc_mul (a b x y : ℕ) (h1: x ∈ linear_combo a b) : x * y ∈ linear_combo a b := 699 | exists.elim h1 700 | (assume c, assume : ∃ d, a * c = b * d + x, 701 | exists.elim this 702 | (assume d, 703 | assume h2: a * c = b * d + x, 704 | have h3: (b*d+x)*y = b*d*y + x*y, from add_mul (b*d) x y, 705 | have h4: a*c*y = b*d*y + x*y, from eq.subst h2.symm h3, 706 | have h5: a*(c*y) = b*d*y + x*y, from eq.subst (mul_assoc a c y) h4, 707 | have h6: a*(c*y) = b*(d*y) + x*y, from eq.subst (mul_assoc b d y) h5, 708 | exists.intro (c*y) (exists.intro (d*y) h6))) 709 | 710 | lemma sub_to_zero (a b : ℕ) (h1: a > b) : b - a = 0 := 711 | have h2: b ≤ a, from le_of_lt h1, 712 | have h3: b - a ≤ a - a, from nat.sub_le_sub_right h2 a, 713 | have h4: a - a = 0, from nat.sub_self a, 714 | have h5: b - a ≤ 0, from eq.subst h4 h3, 715 | eq_bot_iff.mpr h5 716 | 717 | lemma lc_minus_bx (a b e x : ℕ) (h1: e ∈ linear_combo a b) : 718 | (e - b*x) ∈ linear_combo a b := 719 | exists.elim h1 720 | (assume c, assume : ∃ d, a * c = b * d + e, 721 | exists.elim this 722 | (assume d, 723 | assume h2: a * c = b * d + e, 724 | have h3: b*x + e - b*x = e, from nat.sub_eq_of_eq_add rfl, 725 | have h4: a * c = b * d + (b*x + e - b*x), from eq.subst h3.symm h2, 726 | have h5: b*x > e ∨ ¬ (b*x > e), from em(b*x > e), 727 | or.elim h5 728 | (assume h6: b*x > e, 729 | have h7: e - b*x = 0, from sub_to_zero (b*x) e h6, 730 | eq.subst h7.symm (lc_zero a b)) 731 | (assume h8: ¬ (b*x > e), 732 | have h9: b*x ≤ e, from not_lt.mp h8, 733 | have h10: b*x + e - b*x = b*x + (e - b*x), from nat.add_sub_assoc h9 (b*x), 734 | have h11: a * c = b * d + (b*x + (e-b*x)), from eq.subst h10 h4, 735 | have h12: a * c = (b * d + b * x) + (e-b*x), by rw [h11, add_assoc], 736 | have h13: b * d + b * x = b * (d + x), from (mul_add b d x).symm, 737 | have h14: a * c = b * (d + x) + (e-b*x), from eq.subst h13 h12, 738 | exists.intro c (exists.intro (d + x) h14)))) 739 | 740 | lemma lc_minus_ax (a b e x : ℕ) (h1: e ∈ linear_combo a b) : e - a*x ∈ linear_combo a b := 741 | exists.elim h1 742 | (assume c, assume : ∃ d : ℕ, a * c = b * d + e, 743 | exists.elim this 744 | (assume d, 745 | assume h4: a * c = b * d + e, 746 | have h5: a * (c - x) = a * c - a * x, from nat.mul_sub_left_distrib a c x, 747 | have h6: a * (c - x) = (b * d + e) - a*x, from eq.subst h4 h5, 748 | have h7: a*x > e ∨ ¬ (a*x > e), from em(a*x > e), 749 | or.elim h7 750 | (assume h8: a*x > e, 751 | have h9: e - a*x = 0, from sub_to_zero (a*x) e h8, 752 | eq.subst h9.symm (lc_zero a b)) 753 | (assume h10: ¬ (a * x > e), 754 | have h11: a*x ≤ e, from not_lt.mp h10, 755 | have h12: (b * d + e) - a*x = b*d + (e - a*x), from nat.add_sub_assoc h11 (b*d), 756 | have h13: a * (c - x) = b*d + (e - a*x), from eq.subst h12 h6, 757 | exists.intro (c-x) (exists.intro d h13)))) 758 | 759 | theorem lc_add (a b c d : ℕ) (h1: c ∈ linear_combo a b) (h2: d ∈ linear_combo a b) : 760 | (c + d) ∈ linear_combo a b := 761 | exists.elim h1 762 | (assume e, assume : ∃ f : ℕ, a * e = b * f + c, 763 | exists.elim this 764 | (assume f, 765 | assume h3: a * e = b * f + c, 766 | exists.elim h2 767 | (assume g, assume : ∃ h : ℕ, a * g = b * h + d, 768 | exists.elim this 769 | (assume h, 770 | assume h4: a * g = b * h + d, 771 | have h5: a * e + a * g = a * e + a * g, from rfl, 772 | have h6: a * e + a * g = b * f + c + a * g, from eq.subst h3 h5, 773 | have h7: a*e + a*g = b*f + c + (b*h + d), from eq.subst h4 h6, 774 | have h8: a*(e+g) = b*f + c + (b*h + d), from eq.subst (mul_add a e g).symm h7, 775 | have h9: b*f + c + (b*h + d) = (b*f + b*h) + (c+d), 776 | from add_add_add_comm (b*f) c (b*h) d, 777 | have h10: a*(e+g) = (b*f + b*h) + (c+d), from eq.subst h9 h8, 778 | have h11: a*(e+g) = b*(f+h) + (c+d), from eq.subst (mul_add b f h).symm h10, 779 | exists.intro (e+g) (exists.intro (f+h) h11))))) 780 | 781 | lemma lc_comm_subset (a b : ℕ) (h1: b > 0) : linear_combo a b ⊆ linear_combo b a := 782 | assume e, 783 | assume h2: e ∈ linear_combo a b, 784 | exists.elim h2 785 | (assume c, assume : ∃ d, a*c = b*d + e, 786 | exists.elim this 787 | (assume d, 788 | assume h3: a*c = b*d + e, 789 | have h4: e = a*c - b*d, from (nat.sub_eq_of_eq_add h3).symm, 790 | have h5: a ∈ linear_combo b a, from lc_right b a h1, 791 | have h6: a*c ∈ linear_combo b a, from lc_mul b a a c h5, 792 | have h7: a*c - b*d ∈ linear_combo b a, from lc_minus_ax b a (a*c) d h6, 793 | eq.subst h4.symm h7)) 794 | 795 | theorem lc_comm (a b : ℕ) (h1: a > 0) (h2: b > 0) : linear_combo a b = linear_combo b a := 796 | set.eq_of_subset_of_subset 797 | (lc_comm_subset a b h2) 798 | (lc_comm_subset b a h1) 799 | 800 | theorem lc_a_minus (a b e : ℕ) (ha: a > 0) (hb: b > 0) (h1: e ∈ linear_combo a b) : 801 | a - e ∈ linear_combo a b := 802 | exists.elim h1 803 | (assume c, assume : ∃ d, a*c = b*d + e, 804 | exists.elim this 805 | (assume d, 806 | assume h2: a*c = b*d + e, 807 | have h3: a*c - e = a*c - e, from rfl, 808 | have h4: b*d + e - e = a*c - e, from eq.subst h2 h3, 809 | have h5: b*d + e - e = b*d, from (b*d).add_sub_cancel e, 810 | have h6: b*d = a*c - e, from eq.subst h5 h4, 811 | have h7: a*(c-1) = a*c - a*1, from nat.mul_sub_left_distrib a c 1, 812 | have h8: a*(c-1) = a*c - a, by rw [h7, mul_one], 813 | have h9: a*(c-1) + a = a*c - a + a, from congr (congr_arg has_add.add h8) rfl, 814 | have h10: c = 0 ∨ c ≠ 0, from em(c=0), 815 | or.elim h10 816 | (assume h11: c = 0, 817 | have h12: a*0 = 0, from rfl, 818 | have h13: a*c = 0, from eq.subst h11.symm h12, 819 | have h14: 0 = b*d + e, from eq.subst h13 h2, 820 | have h15: e ≤ b*d + e, from nat.le_add_left e (b*d), 821 | have h16: e ≤ 0, from eq.subst h14.symm h15, 822 | have h17: e = 0, from eq_bot_iff.mpr h16, 823 | have h18: a - 0 = a, from rfl, 824 | have h19: a - e = a, from eq.subst h17.symm h18, 825 | eq.subst h19.symm (lc_left a b)) 826 | (assume h20: c ≠ 0, 827 | have h21: c ≥ 1, from bot_lt_iff_ne_bot.mpr h20, 828 | have h22: a*c ≥ a, from nat.le_mul_of_pos_right h21, 829 | have h23: a*c - a + a = a*c, from nat.sub_add_cancel h22, 830 | have h24: a*(c-1) + a = a*c, by rw [h9, h23], 831 | have h25: b*d = a*(c-1) + a - e, from eq.subst h24.symm h6, 832 | have h26: e > a ∨ ¬ (e > a), from em(e > a), 833 | or.elim h26 834 | (assume h27: e > a, 835 | have h28: a - e = 0, from sub_to_zero e a h27, 836 | eq.subst h28.symm (lc_zero a b)) 837 | (assume h29: ¬ e > a, 838 | have h30: e ≤ a, from not_lt.mp h29, 839 | have h31: a*(c-1) + a - e = a*(c-1) + (a-e), from nat.add_sub_assoc h30 (a*(c-1)), 840 | have h32: b*d = a*(c-1) + (a-e), from eq.subst h31 h25, 841 | have h33: (a-e) ∈ linear_combo b a, from exists.intro d (exists.intro (c-1) h32), 842 | eq.subst (lc_comm b a hb ha) h33)))) 843 | 844 | def pos_linear_combo (a b : ℕ) := { e : ℕ | e > 0 ∧ e ∈ linear_combo a b } 845 | 846 | lemma plc_nonempty (a b : ℕ) (h1: a > 0) : (pos_linear_combo a b).nonempty := 847 | have h2: a ∈ linear_combo a b, from lc_left a b, 848 | have h3: a ∈ pos_linear_combo a b, from and.intro h1 h2, 849 | set.nonempty_of_mem h3 850 | 851 | lemma plc_comm_subset (a b : ℕ) (h1: a > 0) (h2: b > 0) : pos_linear_combo a b ⊆ pos_linear_combo b a := 852 | assume e, 853 | assume h3: e ∈ pos_linear_combo a b, 854 | have h4: e ∈ linear_combo b a, from eq.subst (lc_comm a b h1 h2) h3.right, 855 | and.intro h3.left h4 856 | 857 | theorem plc_comm (a b : ℕ) (h1: a > 0) (h2: b > 0) : pos_linear_combo a b = pos_linear_combo b a := 858 | set.eq_of_subset_of_subset 859 | (plc_comm_subset a b h1 h2) 860 | (plc_comm_subset b a h2 h1) 861 | 862 | lemma bezdiv (a b s : ℕ) (h1: a > 0) (h2: b > 0) (h3: is_smallest s (pos_linear_combo a b)) : divides s a := 863 | exists.elim (division a s h3.left.left) 864 | (assume q, assume : ∃ r : ℕ, s * q + r = a ∧ r < s, 865 | exists.elim this 866 | (assume r, 867 | assume h6: s * q + r = a ∧ r < s, 868 | have h7: r = 0 ∨ ¬ r = 0, from em(r=0), 869 | or.elim h7 870 | (assume h8: r = 0, 871 | have h9: s * q + 0 = a, from eq.subst h8 h6.left, 872 | have h10: s * q = a, from h9, 873 | exists.intro q h10) 874 | (assume h11: ¬ r = 0, 875 | have h12: r > 0, from bot_lt_iff_ne_bot.mpr h11, 876 | have h13: a - s*q = r, from nat.sub_eq_of_eq_add (h6.left.symm), 877 | have h14: (s*q) ∈ linear_combo a b, from lc_mul a b s q h3.left.right, 878 | have h15: (a - s*q) ∈ linear_combo a b, from lc_a_minus a b (s*q) h1 h2 h14, 879 | have h16: r ∈ linear_combo a b, from eq.subst h13 h15, 880 | have h17: r ∈ pos_linear_combo a b, from and.intro h12 h16, 881 | have h18: s ≤ r, from h3.right r h17, 882 | have h19: ¬ (r < s), from not_lt.mpr h18, 883 | absurd h6.right h19))) 884 | 885 | theorem bezout (a b : ℕ) (h1: a > 0) (h2: b > 0) (h3: coprime a b) : 1 ∈ linear_combo a b := 886 | have h4: (pos_linear_combo a b).nonempty, from plc_nonempty a b h1, 887 | have h5: ∃ s, is_smallest s (pos_linear_combo a b), from well_ordered (pos_linear_combo a b) h4, 888 | exists.elim h5 889 | (assume s, 890 | assume h6: is_smallest s (pos_linear_combo a b), 891 | have h7: divides s a, from bezdiv a b s h1 h2 h6, 892 | have h8: is_smallest s (pos_linear_combo b a), from eq.subst (plc_comm a b h1 h2) h6, 893 | have h9: divides s b, from bezdiv b a s h2 h1 h8, 894 | have h10: s ∈ codivisors a b, from and.intro h7 h9, 895 | have h11: 1 ≥ s, from h3 s h10, 896 | have h12: s > 0, from h6.left.left, 897 | have h13: s = 1, from le_antisymm h11 h12, 898 | eq.subst h13 h6.left.right) 899 | 900 | def mod : ℕ → ℕ → ℕ 901 | | a m := 902 | if h1 : m ≥ 1 ∧ m ≤ a then 903 | have a - m < a, from nat.sub_lt (le_trans h1.left h1.right) h1.left, 904 | mod (a-m) m 905 | else 906 | a 907 | 908 | lemma mdl (a m : ℕ) (h: m ≥ 1 ∧ m ≤ a) : mod a m = mod (a-m) m := by rw [mod, if_pos h] 909 | 910 | lemma mdr (a m : ℕ) (h: ¬ (m ≥ 1 ∧ m ≤ a)) : mod a m = a := by rw [mod, if_neg h] 911 | 912 | def counterexamples_mod_less (m : ℕ) := { a : ℕ | mod a m ≥ m } 913 | 914 | theorem mod_zero (a: ℕ) : mod a 0 = a := 915 | have h1: ¬ (0 ≥ 1 ∧ 0 ≤ a), from of_to_bool_ff rfl, 916 | mdr a 0 h1 917 | 918 | theorem mod_less (a m : ℕ) (h1: m > 0) : mod a m < m := 919 | have h2: (mod a m < m) ∨ ¬ (mod a m < m), from em(mod a m < m), 920 | or.elim h2 921 | (assume : mod a m < m, this) 922 | (assume h3: ¬ (mod a m < m), 923 | have h4: mod a m ≥ m, from not_lt.mp h3, 924 | have h5: (counterexamples_mod_less m).nonempty, from set.nonempty_of_mem h4, 925 | have h6: ∃ s, is_smallest s (counterexamples_mod_less m), 926 | from well_ordered (counterexamples_mod_less m) h5, 927 | exists.elim h6 928 | (assume s, 929 | assume h7: is_smallest s (counterexamples_mod_less m), 930 | have h8: m ≤ s ∨ ¬ (m ≤ s), from em(m ≤ s), 931 | or.elim h8 932 | (assume h9: m ≤ s, 933 | have h10: m ≥ 1 ∧ m ≤ s, from and.intro h1 h9, 934 | have h11: mod s m = mod (s-m) m, from mdl s m h10, 935 | have h12: mod (s-m) m ≥ m, from eq.subst h11 h7.left, 936 | have h13: s - m ≥ s, from h7.right (s-m) h12, 937 | have h14: s - m < s, from nat.sub_lt_of_pos_le m s h1 h9, 938 | have h15: ¬ (s - m < s), from not_lt.mpr h13, 939 | absurd h14 h15) 940 | (assume h16: ¬ (m ≤ s), 941 | have h17: ¬ (m ≥ 1 ∧ m ≤ s), from not_and_of_not_right (m ≥ 1) h16, 942 | have h18: mod s m = s, from mdr s m h17, 943 | have h19: ¬ (mod s m ≥ m), from eq.subst h18.symm h16, 944 | absurd h7.left h19))) 945 | 946 | theorem mod_cyclic (a m : ℕ) : mod (a + m) m = mod a m := 947 | have h1: m = 0 ∨ m ≠ 0, from em(m=0), 948 | or.elim h1 949 | (assume h2: m = 0, 950 | have h3: a + 0 = a, from rfl, 951 | have h4: a + m = a, from eq.subst h2.symm h3, 952 | show mod (a+m) m = mod a m, by rw [h4]) 953 | (assume h5: m ≠ 0, 954 | have h6: m ≥ 1, from bot_lt_iff_ne_bot.mpr h5, 955 | have h7: m ≤ a + m, from nat.le_add_left m a, 956 | have h8: mod (a+m) m = mod (a+m-m) m, from mdl (a+m) m (and.intro h6 h7), 957 | have h9: a+m-m = a, from nat.add_sub_cancel a m, 958 | show mod (a+m) m = mod a m, by rw [h8, h9]) 959 | 960 | theorem mod_rem (a m r : ℕ) : mod (a*m + r) m = mod r m := 961 | nat.rec_on a 962 | (show mod (0*m + r) m = mod r m, by rw [zero_mul, zero_add]) 963 | (assume a, 964 | assume h1: mod (a*m + r) m = mod r m, 965 | have h2: mod ((a+1)*m + r) m = mod (a*m + r + m) m, 966 | by rw [add_mul, one_mul, add_assoc, (add_comm m r), add_assoc], 967 | have h3: mod (a*m + r + m) m = mod (a*m + r) m, from mod_cyclic (a*m+r) m, 968 | have h4: mod (a*m + r + m) m = mod r m, from eq.subst h1 h3, 969 | eq.subst h4 h2) 970 | 971 | theorem mod_base (a m : ℕ) (h1: a < m) : mod a m = a := 972 | have h2: ¬ (m ≤ a), from not_le.mpr h1, 973 | have h3: ¬ (m ≥ 1 ∧ m ≤ a), from not_and_of_not_right (m ≥ 1) h2, 974 | mdr a m h3 975 | 976 | theorem zero_mod (m: ℕ) : mod 0 m = 0 := 977 | have h1: m = 0 ∨ m ≠ 0, from em(m = 0), 978 | or.elim h1 979 | (assume h2: m = 0, 980 | have h3: mod 0 0 = 0, from mod_zero 0, 981 | show mod 0 m = 0, from eq.subst h2.symm h3) 982 | (assume h4: m ≠ 0, 983 | have h5: 0 < m, from bot_lt_iff_ne_bot.mpr h4, 984 | mod_base 0 m h5) 985 | 986 | lemma mod_div_pos (a m: ℕ) (h1: m > 0) : ∃ q, m*q + mod a m = a := 987 | have h2: ∃ c, ∃ d, m*c + d = a ∧ d < m, from division a m h1, 988 | exists.elim h2 989 | (assume c, 990 | assume: ∃ d, m*c + d = a ∧ d < m, 991 | exists.elim this 992 | (assume d, 993 | assume h3: m*c + d = a ∧ d < m, 994 | have h4: c*m + d = a, from eq.subst (mul_comm m c) h3.left, 995 | have h5: mod (c*m + d) m = mod d m, from mod_rem c m d, 996 | have h6: mod a m = mod d m, from eq.subst h4 h5, 997 | have h7: mod d m = d, from mod_base d m h3.right, 998 | have h8: mod a m = d, by rw [h6, h7], 999 | have h9: m*c + mod a m = a, from eq.subst h8.symm h3.left, 1000 | exists.intro c h9)) 1001 | 1002 | theorem mod_div (a m: ℕ) : ∃ q, m*q + mod a m = a := 1003 | have h1: m = 0 ∨ m ≠ 0, from em(m = 0), 1004 | or.elim h1 1005 | (assume h2: m = 0, 1006 | have h3: mod a m = a, from eq.subst h2.symm (mod_zero a), 1007 | have h4: m*1 + a = a, from eq.subst h2.symm (mul_one a), 1008 | have h5: m*1 + mod a m = a, from eq.subst h3.symm h4, 1009 | exists.intro 1 h5) 1010 | (assume h6: m ≠ 0, 1011 | have h7: m > 0, from bot_lt_iff_ne_bot.mpr h6, 1012 | mod_div_pos a m h7) 1013 | 1014 | theorem zero_mod_divides (a m: ℕ) (h1: mod a m = 0) : divides m a := 1015 | have h2: ∃ q, m*q + mod a m = a, from mod_div a m, 1016 | exists.elim h2 1017 | (assume q, 1018 | assume h3: m*q + mod a m = a, 1019 | have h4: m*q + 0 = a, from eq.subst h1 h3, 1020 | have h5: m*q = a, from h4, 1021 | exists.intro q h5) 1022 | 1023 | theorem mod_nondivisor (a m: ℕ) (h1: ¬ divides m a) : mod a m > 0 := 1024 | have h2: mod a m = 0 ∨ mod a m ≠ 0, from em(mod a m = 0), 1025 | or.elim h2 1026 | (assume h3: mod a m = 0, 1027 | have h4: divides m a, from zero_mod_divides a m h3, 1028 | absurd h4 h1) 1029 | (assume: mod a m ≠ 0, 1030 | bot_lt_iff_ne_bot.mpr this) 1031 | 1032 | def range (n : ℕ) := { x : ℕ | x < n } 1033 | 1034 | def surj (s1 s2 : set ℕ) (f : ℕ → ℕ) := ∀ x2: ℕ, x2 ∈ s2 → ∃ x1: ℕ, x1 ∈ s1 ∧ f x1 = x2 1035 | 1036 | def covers (s1 s2 : set ℕ) := ∃ f: ℕ → ℕ, surj s1 s2 f 1037 | 1038 | def bijects (s1 s2 : set ℕ) := covers s1 s2 ∧ covers s2 s1 1039 | 1040 | def has_size (s : set ℕ) (n : ℕ) := bijects (range n) s 1041 | 1042 | theorem bijects_comm (s1 s2 : set ℕ) (h: bijects s1 s2) : bijects s2 s1 := 1043 | and.intro h.right h.left 1044 | 1045 | def nat_id (n : ℕ) := n 1046 | 1047 | theorem superset_covers (s1 s2 : set ℕ) (h1: s1 ⊇ s2) : covers s1 s2 := 1048 | have h2: surj s1 s2 nat_id ∨ ¬ (surj s1 s2 nat_id), from em(surj s1 s2 nat_id), 1049 | or.elim h2 1050 | (assume: surj s1 s2 nat_id, exists.intro nat_id this) 1051 | (assume h3: ¬ (surj s1 s2 nat_id), 1052 | have h4: ∃ x2 : ℕ, ¬ (x2 ∈ s2 → ∃ x1: ℕ, x1 ∈ s1 ∧ nat_id x1 = x2), 1053 | from classical.not_forall.mp h3, 1054 | exists.elim h4 1055 | (assume x2, 1056 | assume h5: ¬ (x2 ∈ s2 → ∃ x1: ℕ, x1 ∈ s1 ∧ nat_id x1 = x2), 1057 | have h6: x2 ∈ s2 ∧ ¬ (∃ x1: ℕ, x1 ∈ s1 ∧ nat_id x1 = x2), from classical.not_imp.mp h5, 1058 | have h7: nat_id x2 = x2, from rfl, 1059 | have h8: x2 ∈ s1, from set.mem_of_mem_of_subset h6.left h1, 1060 | have h9: ∃ x1: ℕ, x1 ∈ s1 ∧ nat_id x1 = x2, from exists.intro x2 (and.intro h8 h7), 1061 | absurd h9 h6.right)) 1062 | 1063 | theorem covers_rfl (s : set ℕ) : covers s s := 1064 | have h1: s ⊆ s, from set.subset.refl s, 1065 | superset_covers s s h1 1066 | 1067 | theorem bijects_refl (s : set ℕ) : bijects s s := 1068 | have h1: s ⊆ s, from set.subset.refl s, 1069 | have h2: covers s s, from superset_covers s s h1, 1070 | and.intro h2 h2 1071 | 1072 | lemma rzse : range 0 ⊆ ∅ := 1073 | assume x, 1074 | assume h1: x ∈ range 0, 1075 | have h2: x < 0, from h1, 1076 | have h3: ¬ (x < 0), from not_ltz x, 1077 | show x ∈ ∅, from absurd h2 h3 1078 | 1079 | theorem range_zero : range 0 = ∅ := set.eq_empty_of_subset_empty rzse 1080 | 1081 | theorem range_size (n: ℕ) : has_size (range n) n := bijects_refl (range n) 1082 | 1083 | theorem range_subset (a b : ℕ) (h1: a ≤ b) : range a ⊆ range b := 1084 | assume x, 1085 | assume h2: x ∈ range a, 1086 | have h3: x < b, from lt_of_lt_of_le h2 h1, 1087 | show x ∈ range b, from h3 1088 | 1089 | theorem empty_size : has_size ∅ 0 := eq.subst range_zero (range_size 0) 1090 | 1091 | theorem surj_trans (s1 s2 s3 : set ℕ) (f1 f2 : ℕ → ℕ) 1092 | (h1: surj s1 s2 f1) (h2: surj s2 s3 f2) : 1093 | surj s1 s3 (f2 ∘ f1) := 1094 | assume x3, 1095 | assume h3: x3 ∈ s3, 1096 | have h4: ∃ x2: ℕ, x2 ∈ s2 ∧ f2 x2 = x3, from h2 x3 h3, 1097 | exists.elim h4 1098 | (assume x2, 1099 | assume h5: x2 ∈ s2 ∧ f2 x2 = x3, 1100 | have h6: ∃ x1: ℕ, x1 ∈ s1 ∧ f1 x1 = x2, from h1 x2 h5.left, 1101 | exists.elim h6 1102 | (assume x1, 1103 | assume h7: x1 ∈ s1 ∧ f1 x1 = x2, 1104 | have h8: (f2 ∘ f1) x1 = x3, from eq.subst h5.right (congr_arg f2 h7.right), 1105 | exists.intro x1 (and.intro h7.left h8))) 1106 | 1107 | theorem covers_trans (s1 s2 s3 : set ℕ) (h1: covers s1 s2) (h2: covers s2 s3) : 1108 | covers s1 s3 := 1109 | exists.elim h1 1110 | (assume f1, 1111 | assume h3: surj s1 s2 f1, 1112 | exists.elim h2 1113 | (assume f2, 1114 | assume h4: surj s2 s3 f2, 1115 | have h5: surj s1 s3 (f2 ∘ f1), from surj_trans s1 s2 s3 f1 f2 h3 h4, 1116 | exists.intro (f2 ∘ f1) h5)) 1117 | 1118 | theorem bijects_trans (s1 s2 s3 : set ℕ) (h1: bijects s1 s2) (h2: bijects s2 s3) : 1119 | bijects s1 s3 := 1120 | have h3: covers s1 s3, from covers_trans s1 s2 s3 h1.left h2.left, 1121 | have h4: covers s3 s1, from covers_trans s3 s2 s1 h2.right h1.right, 1122 | and.intro h3 h4 1123 | 1124 | def single (n: ℕ) := {x | x = n} 1125 | 1126 | theorem range_one : range 1 = single 0 := 1127 | set.eq_of_subset_of_subset 1128 | (assume x, 1129 | assume h1: x ∈ range 1, 1130 | have h2: x ≤ 0, from nat.lt_succ_iff.mp h1, 1131 | have h3: x = 0, from eq_bot_iff.mpr h2, 1132 | show x ∈ single 0, from h3) 1133 | (assume x, 1134 | assume h4: x ∈ single 0, 1135 | have h5: x < 1, from eq.subst (h4.symm) (lt_add_one 0), 1136 | show x ∈ range 1, from h5) 1137 | 1138 | theorem single_covers (a b : ℕ) : covers (single a) (single b) := 1139 | have h1: ∃ x1: ℕ, x1 ∈ single a ∧ (λ x: ℕ, b) a = b, from exists_eq_left.mpr rfl, 1140 | have h2: ∀ x2: ℕ, x2 ∈ single b → ∃ x1: ℕ, x1 ∈ single a ∧ (λ x: ℕ, b) a = x2, from 1141 | (assume x2, 1142 | assume h3: x2 ∈ single b, 1143 | have h4: x2 = b, from h3, 1144 | eq.subst h4.symm h1), 1145 | have h5: surj (single a) (single b) (λ x: ℕ, b), from h2, 1146 | exists.intro (λ x : ℕ, b) h5 1147 | 1148 | theorem single_bijects (a b : ℕ) : bijects (single a) (single b) := 1149 | and.intro (single_covers a b) (single_covers b a) 1150 | 1151 | theorem single_size (a: ℕ) : has_size (single a) 1 := 1152 | have h1: bijects (single 0) (single a), from single_bijects 0 a, 1153 | have h2: bijects (range 1) (single a), from eq.subst range_one.symm h1, 1154 | h2 1155 | 1156 | theorem bijects_size (s1 s2 : set ℕ) (h1: bijects s1 s2) (n: ℕ) (h2: has_size s1 n) : 1157 | has_size s2 n := 1158 | have h3: bijects (range n) s1, from h2, 1159 | have h4: bijects (range n) s2, from bijects_trans (range n) s1 s2 h3 h1, 1160 | h4 1161 | 1162 | theorem surj_empty (s : set ℕ) (f : ℕ → ℕ) : surj s (∅: set ℕ) f := 1163 | assume x2: ℕ, 1164 | assume h1: x2 ∈ (∅: set ℕ), 1165 | have h2: x2 ∉ (∅: set ℕ), from not_false, 1166 | absurd h1 h2 1167 | 1168 | theorem covers_empty (s : set ℕ) : covers s (∅: set ℕ) := 1169 | have h1: surj s (∅: set ℕ) nat_id, from surj_empty s nat_id, 1170 | exists.intro nat_id h1 1171 | 1172 | theorem surj_nonempty (s1 s2: set ℕ) (f: ℕ → ℕ) (h1: surj s1 s2 f) (h2: s2.nonempty) : 1173 | s1.nonempty := 1174 | have h3: ∃ x2: ℕ, x2 ∈ s2, from h2, 1175 | exists.elim h3 1176 | (assume x2, 1177 | assume h4: x2 ∈ s2, 1178 | have h5: ∃ x1: ℕ, x1 ∈ s1 ∧ f x1 = x2, from h1 x2 h4, 1179 | exists.elim h5 1180 | (assume x1, 1181 | assume h6: x1 ∈ s1 ∧ f x1 = x2, 1182 | set.nonempty_of_mem h6.left)) 1183 | 1184 | theorem empty_surj (s : set ℕ) (f : ℕ → ℕ) (h1: surj (∅: set ℕ) s f) : s = ∅ := 1185 | have h2: s = ∅ ∨ s.nonempty, from set.eq_empty_or_nonempty s, 1186 | or.elim h2 1187 | (assume: s = ∅, this) 1188 | (assume h3: s.nonempty, 1189 | have h4: (∅: set ℕ).nonempty, from surj_nonempty ∅ s f h1 h3, 1190 | absurd h4 exists_false) 1191 | 1192 | theorem empty_covers (s : set ℕ) (h1: covers (∅: set ℕ) s) : s = ∅ := 1193 | exists.elim h1 1194 | (assume f: ℕ → ℕ, 1195 | assume h2: surj (∅: set ℕ) s f, 1196 | empty_surj s f h2) 1197 | 1198 | theorem size_zero_empty (s: set ℕ) (h1: has_size s 0) : s = ∅ := 1199 | have h2: covers (range 0) s, from h1.left, 1200 | have h3: covers ∅ s, from eq.subst range_zero h2, 1201 | empty_covers s h3 1202 | 1203 | theorem covers_nonempty (s1 s2: set ℕ) (h1: covers s1 s2) (h2: s2.nonempty) : 1204 | s1.nonempty := 1205 | exists.elim h1 1206 | (assume f: ℕ → ℕ, 1207 | assume h3: surj s1 s2 f, 1208 | surj_nonempty s1 s2 f h3 h2) 1209 | 1210 | theorem bijects_empty (s: set ℕ) (h1: bijects s ∅) : s = ∅ := 1211 | empty_covers s h1.right 1212 | 1213 | theorem bijects_nonempty (s1 s2: set ℕ) (h1: bijects s1 s2) (h2: s2.nonempty) : 1214 | s1.nonempty := 1215 | covers_nonempty s1 s2 h1.left h2 1216 | 1217 | theorem range_nonempty (n: ℕ) (h1: n > 0) : (range n).nonempty := 1218 | have h2: 0 ∈ range n, from h1, 1219 | set.nonempty_of_mem h2 1220 | 1221 | theorem pos_size (n : ℕ) (s: set ℕ) (h1: n > 0) (h2: has_size s n) : s.nonempty := 1222 | have h3: bijects s (range n), from bijects_comm (range n) s h2, 1223 | have h4: (range n).nonempty, from range_nonempty n h1, 1224 | bijects_nonempty s (range n) h3 h4 1225 | 1226 | def remove (s: set ℕ) (a: ℕ) := {x | x ∈ s ∧ x ≠ a} 1227 | 1228 | lemma remove_nmem (s: set ℕ) (a: ℕ) (h1: a ∉ s) : remove s a = s := 1229 | set.diff_singleton_eq_self h1 1230 | 1231 | theorem remove_subset (s: set ℕ) (a: ℕ) : (remove s a) ⊆ s := 1232 | set.sep_subset s (λ x: ℕ, x ≠ a) 1233 | 1234 | theorem remove_union (s1 s2: set ℕ) (x: ℕ) : 1235 | remove (s1 ∪ s2) x = (remove s1 x) ∪ (remove s2 x) := set.union_diff_distrib 1236 | 1237 | lemma surj_dec (a: ℕ) (s1 s2: set ℕ) (f: ℕ → ℕ) (h2: surj s1 s2 f): 1238 | surj (remove s1 a) (remove s2 (f a)) f := 1239 | assume x2, 1240 | assume h4: x2 ∈ (remove s2 (f a)), 1241 | have h5: x2 ∈ s2, from h4.left, 1242 | have h6: ∃ x1: ℕ, x1 ∈ s1 ∧ f x1 = x2, from h2 x2 h5, 1243 | exists.elim h6 1244 | (assume x1, 1245 | assume h7: x1 ∈ s1 ∧ f x1 = x2, 1246 | have h8: x2 ≠ f a, from h4.right, 1247 | have h9: f x1 ≠ f a, from eq.subst h7.right.symm h8, 1248 | have x1 = a ∨ x1 ≠ a, from em(x1 = a), 1249 | or.elim this 1250 | (assume h10: x1 = a, 1251 | have h11: f x1 = f a, from congr_arg f h10, 1252 | absurd h11 h9) 1253 | (assume h12: x1 ≠ a, 1254 | have h13: x1 ∈ (remove s1 a), from and.intro h7.left h12, 1255 | exists.intro x1 (and.intro h13 h7.right))) 1256 | 1257 | def swap : ℕ → ℕ → ℕ → ℕ 1258 | | a b c := 1259 | if a = c then b else (if b = c then a else c) 1260 | 1261 | lemma swapl (a b : ℕ) : swap a b a = b := if_pos rfl 1262 | 1263 | lemma swapr (a b : ℕ) : swap a b b = a := 1264 | have h1: a = b ∨ a ≠ b, from em(a=b), 1265 | or.elim h1 1266 | (assume h2: a = b, 1267 | have h3: swap a a a = a, from swapl a a, 1268 | have h4: swap a b b = a, from eq.subst h2 h3, 1269 | h4) 1270 | (assume h5: a ≠ b, 1271 | have h6: swap a b b = (if b = b then a else b), from if_neg h5, 1272 | have h7: (if b = b then a else b) = a, from if_pos rfl, 1273 | have h8: swap a b b = a, by rw [h6, h7], 1274 | h8) 1275 | 1276 | lemma swapne (a b c : ℕ) (h1: a ≠ c) (h2: b ≠ c) : swap a b c = c := 1277 | have h3: swap a b c = (if b = c then a else c), from if_neg h1, 1278 | have h4: (if b = c then a else c) = c, from if_neg h2, 1279 | by rw [h3, h4] 1280 | 1281 | lemma surj_swap (a b : ℕ) (s : set ℕ) (h2: b ∈ s) (h3: a ≠ b) : 1282 | surj (remove s a) (remove s b) (swap a b) := 1283 | assume x2, 1284 | assume h4: x2 ∈ (remove s b), 1285 | have h5: x2 = a ∨ x2 ≠ a, from em(x2 = a), 1286 | or.elim h5 1287 | (assume h6: x2 = a, 1288 | have h7: b ∈ (remove s a), from set.mem_sep h2 h3.symm, 1289 | have h8: (swap a b) b = x2, from eq.subst h6.symm (swapr a b), 1290 | exists.intro b (and.intro h7 h8)) 1291 | (assume h9: x2 ≠ a, 1292 | have h10: x2 ∈ (remove s a), from and.intro h4.left h9, 1293 | have h11: x2 ≠ b, from h4.right, 1294 | have h12: (swap a b) x2 = x2, from swapne a b x2 h9.symm h11.symm, 1295 | exists.intro x2 (and.intro h10 h12)) 1296 | 1297 | lemma covers_swap (a b : ℕ) (s : set ℕ) (h2: b ∈ s) : 1298 | covers (remove s a) (remove s b) := 1299 | have h3: a = b ∨ a ≠ b, from em(a = b), 1300 | or.elim h3 1301 | (assume h4: a = b, 1302 | have h5: covers (remove s a) (remove s a), from covers_rfl (remove s a), 1303 | have h6: remove s b = remove s a, from congr_arg (remove s) h4.symm, 1304 | have h7: remove s b ⊆ remove s a, from (set.subset.antisymm_iff.mp h6).left, 1305 | superset_covers (remove s a) (remove s b) h7) 1306 | (assume h8: a ≠ b, 1307 | exists.intro (swap a b) (surj_swap a b s h2 h8)) 1308 | 1309 | lemma bijects_swap (a b : ℕ) (s : set ℕ) (h1: a ∈ s) (h2: b ∈ s): 1310 | bijects (remove s a) (remove s b) := 1311 | and.intro (covers_swap a b s h2) (covers_swap b a s h1) 1312 | 1313 | lemma rrn (n: ℕ) : range n = remove (range (n+1)) n := 1314 | set.eq_of_subset_of_subset 1315 | (assume x, 1316 | assume h1: x ∈ range n, 1317 | have h2: x < n + 1, from nat.lt.step h1, 1318 | have h3: x ≠ n, from ne_of_lt h1, 1319 | and.intro h2 h3) 1320 | (assume x, 1321 | assume h4: x ∈ remove (range (n+1)) n, 1322 | have h5: x < n + 1, from h4.left, 1323 | have h6: x ≠ n, from h4.right, 1324 | array.push_back_idx h5 h6) 1325 | 1326 | lemma range_remove (a n : ℕ) (h1: a ∈ range (n+1)) : 1327 | bijects (range n) (remove (range (n+1)) a) := 1328 | have h2: n ∈ range (n+1), from lt_add_one n, 1329 | have h3: bijects (remove (range (n+1)) n) (remove (range (n+1)) a), 1330 | from bijects_swap n a (range (n+1)) h2 h1, 1331 | eq.subst (rrn n).symm h3 1332 | 1333 | def overcovers (n: ℕ) := covers (range n) (range (n+1)) 1334 | 1335 | lemma noc_zero : ¬ overcovers 0 := 1336 | have h1: overcovers 0 ∨ ¬ overcovers 0, from em(overcovers 0), 1337 | or.elim h1 1338 | (assume h2: overcovers 0, 1339 | have h3: covers ∅ (range 1), from eq.subst range_zero h2, 1340 | have h4: (range 1) = ∅, from empty_covers (range 1) h3, 1341 | have h5: 1 > 0, from lt_add_one 0, 1342 | have h6: (range 1).nonempty, from range_nonempty 1 h5, 1343 | have h7: ¬ (range 1 = ∅), from set.nmem_singleton_empty.mpr h6, 1344 | absurd h4 h7) 1345 | (assume: ¬ overcovers 0, this) 1346 | 1347 | lemma ocai (n: ℕ) (h1: overcovers (n+1)) : overcovers n := 1348 | exists.elim h1 1349 | (assume f, 1350 | assume h2: ∀ x2: ℕ, x2 ∈ range ((n+1)+1) → ∃ x1: ℕ, x1 ∈ range (n+1) ∧ f x1 = x2, 1351 | have h3: n+1 ∈ range((n+1)+1), from lt_add_one (n+1), 1352 | have h4: ∃ x1: ℕ, x1 ∈ range (n+1) ∧ f x1 = (n+1), from h2 (n+1) h3, 1353 | exists.elim h4 1354 | (assume x1, 1355 | assume h5: x1 ∈ range (n+1) ∧ f x1 = (n+1), 1356 | have h6: surj (remove (range (n+1)) x1) (remove (range ((n+1)+1)) (f x1)) f, 1357 | from surj_dec x1 (range (n+1)) (range ((n+1)+1)) f h2, 1358 | have h7: range (n+1) = remove (range ((n+1)+1)) (f x1), 1359 | from eq.subst h5.right.symm (rrn (n+1)), 1360 | have h8: bijects (range n) (remove (range (n+1)) x1) , from range_remove x1 n h5.left, 1361 | have h9: covers (remove (range (n+1)) x1) (remove (range ((n+1)+1)) (f x1)), 1362 | from exists.intro f h6, 1363 | have h10: covers (range n) (remove (range ((n+1)+1)) (f x1)), 1364 | from covers_trans (range n) (remove (range (n+1)) x1) 1365 | (remove (range ((n+1)+1)) (f x1)) h8.left h9, 1366 | have h11: covers (range n) (range (n+1)), from eq.subst h7.symm h10, 1367 | h11)) 1368 | 1369 | lemma noc_any (n: ℕ) : ¬ overcovers n := 1370 | nat.rec_on n 1371 | (noc_zero) 1372 | (assume n, 1373 | assume h1: ¬ overcovers n, 1374 | have h2: overcovers (n+1) ∨ ¬ overcovers (n+1), from em(overcovers (n+1)), 1375 | or.elim h2 1376 | (assume h3: overcovers (n+1), 1377 | have h4: overcovers n, from ocai n h3, 1378 | absurd h4 h1) 1379 | (assume: ¬ overcovers (n+1), this)) 1380 | 1381 | lemma suhelp (s: set ℕ) (a b: ℕ) (h1: has_size s a) (h2: has_size s b) (h3: a > b) : 1382 | a = b := 1383 | have h4: bijects (range a) s, from h1, 1384 | have h5: bijects (range b) s, from h2, 1385 | have h6: bijects s (range b), from bijects_comm (range b) s h5, 1386 | have h7: bijects (range a) (range b), from bijects_trans (range a) s (range b) h4 h6, 1387 | have h8: b+1 ≤ a, from h3, 1388 | have h9: range (b+1) ⊆ range a, from range_subset (b+1) a h8, 1389 | have h10: covers (range a) (range (b+1)), from superset_covers (range a) (range (b+1)) h9, 1390 | have h11: covers (range b) (range (b+1)), 1391 | from covers_trans (range b) (range a) (range (b+1)) h7.right h10, 1392 | absurd h11 (noc_any b) 1393 | 1394 | theorem size_unique (s : set ℕ) (a b : ℕ) (h1: has_size s a) (h2: has_size s b) : a = b := 1395 | have h3: a < b ∨ ¬ (a < b), from em(a < b), 1396 | or.elim h3 1397 | (assume h4: a < b, 1398 | have h6: b = a, from suhelp s b a h2 h1 h4, 1399 | h6.symm) 1400 | (assume h7: ¬ (a < b), 1401 | have h8: a = b ∨ a > b, from eq_or_lt_of_not_lt h7, 1402 | or.elim h8 1403 | (assume: a = b, this) 1404 | (assume h9: a > b, 1405 | suhelp s a b h1 h2 h9)) 1406 | 1407 | theorem covers_remove (s1 s2: set ℕ) (x1 x2: ℕ) (h2: x2 ∈ s2) (h3: covers s1 s2) : 1408 | covers (remove s1 x1) (remove s2 x2) := 1409 | exists.elim h3 1410 | (assume f, 1411 | assume h4: surj s1 s2 f, 1412 | have h5: surj (remove s1 x1) (remove s2 (f x1)) f, from surj_dec x1 s1 s2 f h4, 1413 | have h6: covers (remove s1 x1) (remove s2 (f x1)), from exists.intro f h5, 1414 | have h7: covers (remove s2 (f x1)) (remove s2 x2), from covers_swap (f x1) x2 s2 h2, 1415 | covers_trans (remove s1 x1) (remove s2 (f x1)) (remove s2 x2) h6 h7) 1416 | 1417 | theorem bijects_remove (s1 s2: set ℕ) (x1 x2: ℕ) (h1: x1 ∈ s1) (h2: x2 ∈ s2) 1418 | (h3: bijects s1 s2) : 1419 | bijects (remove s1 x1) (remove s2 x2) := 1420 | and.intro (covers_remove s1 s2 x1 x2 h2 h3.left) (covers_remove s2 s1 x2 x1 h1 h3.right) 1421 | 1422 | theorem remove_size (s: set ℕ) (a n: ℕ) (h1: has_size s (n+1)) (h2: a ∈ s) : 1423 | has_size (remove s a) n := 1424 | have h3: bijects (range (n+1)) s, from h1, 1425 | have h4: n ∈ range (n+1), from lt_add_one n, 1426 | have h5: bijects (remove (range (n+1)) n) (remove s a), 1427 | from bijects_remove (range (n+1)) s n a h4 h2 h3, 1428 | have h6: bijects (range n) (remove s a), from eq.subst (rrn n).symm h5, 1429 | h6 1430 | 1431 | def patch : (ℕ → ℕ) → ℕ → ℕ → ℕ → ℕ 1432 | | f a b x := 1433 | if x = a then b else (f x) 1434 | 1435 | lemma patchl (f: ℕ → ℕ) (a b : ℕ) : patch f a b a = b := if_pos rfl 1436 | lemma patchr (f: ℕ → ℕ) (a b x : ℕ) (h1: x ≠ a) : patch f a b x = f x := if_neg h1 1437 | 1438 | lemma surj_insert (a: ℕ) (s1 s2: set ℕ) (f: ℕ → ℕ) (h1: a ∈ s1) 1439 | (h2: surj (remove s1 a) (remove s2 (f a)) f) : 1440 | surj s1 s2 f := 1441 | assume x2, 1442 | assume h3: x2 ∈ s2, 1443 | have h4: x2 = (f a) ∨ x2 ≠ (f a), from em(x2 = (f a)), 1444 | or.elim h4 1445 | (assume h5: x2 = (f a), 1446 | have h6: a ∈ s1 ∧ f a = x2, from and.intro h1 h5.symm, 1447 | exists.intro a h6) 1448 | (assume h7: x2 ≠ (f a), 1449 | have h8: x2 ∈ remove s2 (f a), from set.mem_sep h3 h7, 1450 | have h9: ∃ x1: ℕ, x1 ∈ (remove s1 a) ∧ f x1 = x2, from h2 x2 h8, 1451 | exists.elim h9 1452 | (assume x1, 1453 | assume h10: x1 ∈ (remove s1 a) ∧ f x1 = x2, 1454 | have h11: x1 ∈ s1, from h10.left.left, 1455 | exists.intro x1 (and.intro h11 h10.right))) 1456 | 1457 | lemma surj_patch (a b: ℕ) (s1 s2: set ℕ) (f: ℕ → ℕ) (h1: a ∉ s1) (h2: surj s1 s2 f) : 1458 | surj s1 s2 (patch f a b) := 1459 | assume x2, 1460 | assume h3: x2 ∈ s2, 1461 | have h4: ∃ x1: ℕ, x1 ∈ s1 ∧ f x1 = x2, from h2 x2 h3, 1462 | exists.elim h4 1463 | (assume x1, 1464 | assume h5: x1 ∈ s1 ∧ f x1 = x2, 1465 | have h6: x1 = a ∨ x1 ≠ a, from em(x1 = a), 1466 | or.elim h6 1467 | (assume h7: x1 = a, 1468 | have h8: x1 ∉ s1, from eq.subst h7.symm h1, 1469 | absurd h5.left h8) 1470 | (assume h9: x1 ≠ a, 1471 | have h10: (patch f a b) x1 = x2, from eq.subst h5.right (patchr f a b x1 h9), 1472 | exists.intro x1 (and.intro h5.left h10))) 1473 | 1474 | theorem covers_insert (s1 s2: set ℕ) (x1 x2: ℕ) (h1: x1 ∈ s1) 1475 | (h2: covers (remove s1 x1) (remove s2 x2)) : 1476 | covers s1 s2 := 1477 | exists.elim h2 1478 | (assume f, 1479 | assume h3: surj (remove s1 x1) (remove s2 x2) f, 1480 | have h4: x1 ∉ (remove s1 x1), from not_and_not_right.mpr (congr_fun rfl), 1481 | have h5: surj (remove s1 x1) (remove s2 x2) (patch f x1 x2), 1482 | from surj_patch x1 x2 (remove s1 x1) (remove s2 x2) f h4 h3, 1483 | have h6: surj (remove s1 x1) (remove s2 ((patch f x1 x2) x1)) (patch f x1 x2), 1484 | from eq.subst (patchl f x1 x2).symm h5, 1485 | have h7: surj s1 s2 (patch f x1 x2), from surj_insert x1 s1 s2 (patch f x1 x2) h1 h6, 1486 | exists.intro (patch f x1 x2) h7) 1487 | 1488 | theorem bijects_insert (s1 s2: set ℕ) (x1 x2: ℕ) (h1: x1 ∈ s1) (h2: x2 ∈ s2) 1489 | (h3: bijects (remove s1 x1) (remove s2 x2)) : 1490 | bijects s1 s2 := 1491 | and.intro (covers_insert s1 s2 x1 x2 h1 h3.left) (covers_insert s2 s1 x2 x1 h2 h3.right) 1492 | 1493 | theorem insert_size (s: set ℕ) (a n: ℕ) (h1: a ∈ s) (h2: has_size (remove s a) n) : 1494 | has_size s (n+1) := 1495 | have h3: bijects (range n) (remove s a), from h2, 1496 | have h4: n ∈ range (n+1), from lt_add_one n, 1497 | have h5: bijects (remove (range (n+1)) n) (remove s a), from eq.subst (rrn n) h3, 1498 | bijects_insert (range (n+1)) s n a h4 h1 h5 1499 | 1500 | def rcfn (n: ℕ) := ∀ s: set ℕ, covers (range n) s → ∃ a: ℕ, has_size s a 1501 | 1502 | lemma rcfz : rcfn 0 := 1503 | assume s, 1504 | assume h1: covers (range 0) s, 1505 | have h2: covers ∅ s, from eq.subst range_zero h1, 1506 | have h3: s = ∅, from empty_covers s h2, 1507 | have h4: has_size s 0, from eq.subst h3.symm empty_size, 1508 | exists.intro 0 h4 1509 | 1510 | lemma range_covers_finite (n: ℕ) : rcfn n := 1511 | nat.rec_on n 1512 | (rcfz) 1513 | (assume n, 1514 | assume h1: rcfn n, 1515 | assume s, 1516 | assume h2: covers (range (n+1)) s, 1517 | exists.elim h2 1518 | (assume f, 1519 | assume h3: surj (range (n+1)) s f, 1520 | have h5: surj (remove (range (n+1)) n) (remove s (f n)) f, 1521 | from surj_dec n (range (n+1)) s f h3, 1522 | have h6: surj (range n) (remove s (f n)) f, from eq.subst (rrn n).symm h5, 1523 | have h7: covers (range n) (remove s (f n)), from exists.intro f h6, 1524 | have h8: ∃ a: ℕ, has_size (remove s (f n)) a, from h1 (remove s (f n)) h7, 1525 | exists.elim h8 1526 | (assume a, 1527 | assume h9: has_size (remove s (f n)) a, 1528 | have h10: (f n) ∈ s ∨ (f n) ∉ s, from em((f n) ∈ s), 1529 | or.elim h10 1530 | (assume h11: (f n) ∈ s, 1531 | have h12: has_size s (a+1), from insert_size s (f n) a h11 h9, 1532 | exists.intro (a+1) h12) 1533 | (assume h13: (f n) ∉ s, 1534 | have h14: (remove s (f n)) = s, from remove_nmem s (f n) h13, 1535 | have h15: has_size s a, from eq.subst h14 h9, 1536 | exists.intro a h15)))) 1537 | 1538 | 1539 | theorem subset_finite (s1 s2: set ℕ) (n2: ℕ) (h1: s1 ⊆ s2) (h2: has_size s2 n2) : 1540 | ∃ n1: ℕ, has_size s1 n1 := 1541 | have h3: covers (range n2) s2, from h2.left, 1542 | have h4: covers s2 s1, from superset_covers s2 s1 h1, 1543 | have h5: covers (range n2) s1, from covers_trans (range n2) s2 s1 h3 h4, 1544 | range_covers_finite n2 s1 h5 1545 | 1546 | def ssn (n1: ℕ) := ∀ s1: set ℕ, ∀ s2: set ℕ, ∀ n2: ℕ, 1547 | has_size s1 n1 ∧ has_size s2 n2 ∧ s1 ∩ s2 = ∅ → has_size (s1 ∪ s2) (n1 + n2) 1548 | 1549 | lemma ssnz : ssn 0 := 1550 | assume s1, 1551 | assume s2, 1552 | assume n2, 1553 | assume h1: has_size s1 0 ∧ has_size s2 n2 ∧ s1 ∩ s2 = ∅, 1554 | have h2: s1 = ∅, from size_zero_empty s1 h1.left, 1555 | have h3: ∅ ∪ s2 = s2, from set.empty_union s2, 1556 | have h4: s1 ∪ s2 = s2, from eq.subst h2.symm h3, 1557 | have h5: 0 + n2 = n2, from mul_one n2, 1558 | have h6: has_size (s1 ∪ s2) n2, from eq.subst h4.symm h1.right.left, 1559 | eq.subst h5.symm h6 1560 | 1561 | lemma nmem_nonint (s1 s2: set ℕ) (x1: ℕ) (h1: x1 ∈ s1) (h2: s1 ∩ s2 = ∅) : x1 ∉ s2 := 1562 | have h3: x1 ∈ s2 ∨ x1 ∉ s2, from em(x1 ∈ s2), 1563 | or.elim h3 1564 | (assume h4: x1 ∈ s2, 1565 | have h5: x1 ∈ s1 ∩ s2, from set.mem_sep h1 h4, 1566 | have h6: x1 ∉ ∅, from list.not_mem_nil x1, 1567 | have h7: x1 ∉ s1 ∩ s2, from eq.subst h2.symm h6, 1568 | absurd h5 h7) 1569 | (assume: x1 ∉ s2, this) 1570 | 1571 | lemma ssni (n1: ℕ) (h1: ssn n1) : ssn (n1+1) := 1572 | assume s1, 1573 | assume s2, 1574 | assume n2, 1575 | assume h2: has_size s1 (n1+1) ∧ has_size s2 n2 ∧ s1 ∩ s2 = ∅, 1576 | have h3: covers s1 (range (n1+1)), from h2.left.right, 1577 | have h4: n1+1 > 0, from nat.succ_pos n1, 1578 | have h5: (range (n1+1)).nonempty, from range_nonempty (n1+1) h4, 1579 | have h6: s1.nonempty, from covers_nonempty s1 (range (n1+1)) h3 h5, 1580 | have h7: ∃ x, x ∈ s1, from h6, 1581 | exists.elim h7 1582 | (assume x, 1583 | assume h8: x ∈ s1, 1584 | have h9: has_size (remove s1 x) n1, from remove_size s1 x n1 h2.left h8, 1585 | have h10: (remove s1 x) ⊆ s1, from remove_subset s1 x, 1586 | have h11: (remove s1 x) ∩ s2 ⊆ s1 ∩ s2, from set.inter_subset_inter_left s2 h10, 1587 | have h12: (remove s1 x) ∩ s2 ⊆ ∅, from eq.subst h2.right.right h11, 1588 | have h13: (remove s1 x) ∩ s2 = ∅, from set.subset_eq_empty h12 rfl, 1589 | have h14: has_size ((remove s1 x) ∪ s2) (n1 + n2), 1590 | from h1 (remove s1 x) s2 n2 (and.intro h9 (and.intro h2.right.left h13)), 1591 | have h15: remove (s1 ∪ s2) x = (remove s1 x) ∪ (remove s2 x), from remove_union s1 s2 x, 1592 | have h16: x ∉ s2, from nmem_nonint s1 s2 x h8 h2.right.right, 1593 | have h17: (remove s2 x) = s2, from remove_nmem s2 x h16, 1594 | have h18: remove (s1 ∪ s2) x = (remove s1 x) ∪ s2, 1595 | from eq.trans h15 (congr_arg (has_union.union (remove s1 x)) h17), 1596 | have h19: has_size (remove (s1 ∪ s2) x) (n1 + n2), from eq.subst h18.symm h14, 1597 | have h20: x ∈ s1 ∪ s2, from set.mem_union_left s2 h8, 1598 | have h21: has_size (s1 ∪ s2) (n1 + n2 + 1), 1599 | from insert_size (s1 ∪ s2) x (n1 + n2) h20 h19, 1600 | have h22: (n1+1) + n2 = (n1 + n2 + 1), from nat.succ_add n1 n2, 1601 | eq.subst h22.symm h21) 1602 | 1603 | lemma ssn_any (n: ℕ) : ssn n := 1604 | nat.rec_on n 1605 | (ssnz) 1606 | (assume n, 1607 | assume h1: ssn n, 1608 | ssni n h1) 1609 | 1610 | theorem size_sum (s1 s2: set ℕ) (n1 n2: ℕ) (h1: has_size s1 n1) (h2: has_size s2 n2) 1611 | (h3: s1 ∩ s2 = ∅) : has_size (s1 ∪ s2) (n1 + n2) := 1612 | ssn_any n1 s1 s2 n2 (and.intro h1 (and.intro h2 h3)) 1613 | 1614 | def set_mod_mult (s: set ℕ) (a m: ℕ) := { c | ∃ b: ℕ, b ∈ s ∧ mod (a*b) m = c } 1615 | 1616 | def prange (n: ℕ) := remove (range n) 0 1617 | 1618 | theorem prange_pos (a b: ℕ) (h1: a ∈ prange b) : a > 0 := 1619 | have h2: a ≠ 0, from h1.right, 1620 | bot_lt_iff_ne_bot.mpr h2 1621 | 1622 | theorem prange_coprime (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : coprime x p := 1623 | have h3: coprime x p ∨ ¬ coprime x p, from em(coprime x p), 1624 | or.elim h3 1625 | (assume: coprime x p, this) 1626 | (assume: ¬ coprime x p, 1627 | have h4: ∃ y, y > 1 ∧ divides y x ∧ divides y p, from not_coprime x p this, 1628 | exists.elim h4 1629 | (assume y, 1630 | assume h5: y > 1 ∧ divides y x ∧ divides y p, 1631 | have h6: y = 1 ∨ y = p, from prime_divisors y p h1 h5.right.right, 1632 | have h7: y ≠ 1, from ne_of_gt h5.left, 1633 | have h8: y = p, from or.resolve_left h6 h7, 1634 | have h9: divides p x, from eq.subst h8 h5.right.left, 1635 | have h11: x > 0, from prange_pos x p h2, 1636 | have h12: p ≤ x, from divides_le p x h9 h11, 1637 | have h13: x < p, from h2.left, 1638 | have h14: ¬ (x < p), from not_lt.mpr h12, 1639 | absurd h13 h14)) 1640 | 1641 | theorem prange_nondivisor (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : ¬ divides p x := 1642 | have h3: coprime x p, from prange_coprime x p h1 h2, 1643 | have h4: divides p x ∨ ¬ divides p x, from em(divides p x), 1644 | or.elim h4 1645 | (assume h5: divides p x, 1646 | have h6: divides p p, from divides_self p, 1647 | have h7: ¬ coprime x p, from div_not_coprime p x p h1 h5 h6, 1648 | absurd h3 h7) 1649 | (assume: ¬ divides p x, this) 1650 | 1651 | theorem prange_closed (x y p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) (h3: y ∈ prange p) : 1652 | mod (x*y) p ∈ prange p := 1653 | have h4: coprime x p, from prange_coprime x p h1 h2, 1654 | have h5: coprime y p, from prange_coprime y p h1 h3, 1655 | have h6: x > 0, from prange_pos x p h2, 1656 | have h7: y > 0, from prange_pos y p h3, 1657 | have h8: p > 0, from prime_pos p h1, 1658 | have h9: coprime p (x*y), from coprime_mult p x y h8 h6 h7 (coprime_comm x p h4) (coprime_comm y p h5), 1659 | have h10: coprime (x*y) p, from coprime_comm p (x*y) h9, 1660 | have h11: divides p (x*y) ∨ ¬ divides p (x*y), from em(divides p (x*y)), 1661 | or.elim h11 1662 | (assume h12: divides p (x*y), 1663 | have h13: divides p x ∨ divides p y, from euclids_lemma p x y h1 h12, 1664 | or.elim h13 1665 | (assume: divides p x, 1666 | absurd this (prange_nondivisor x p h1 h2)) 1667 | (assume: divides p y, 1668 | absurd this (prange_nondivisor y p h1 h3))) 1669 | (assume h14: ¬ divides p (x*y), 1670 | have h15: mod (x*y) p > 0, from mod_nondivisor (x*y) p h14, 1671 | have h16: mod (x*y) p < p, from mod_less (x*y) p h8, 1672 | have h17: mod (x*y) p ≠ 0, from ne_of_gt h15, 1673 | and.intro h16 h17) 1674 | 1675 | theorem mod_rmult (a b m: ℕ): mod (a * (mod b m)) m = mod (a*b) m := 1676 | have h1: ∃ q, m*q + mod b m = b, from mod_div b m, 1677 | exists.elim h1 1678 | (assume q, 1679 | assume h2: m*q + mod b m = b, 1680 | have h3: mod (a*b) m = mod (a*(m*q + mod b m)) m, from eq.subst h2.symm rfl, 1681 | have h4: a*(m*q + mod b m) = (a*q)*m + a*(mod b m), by rw [mul_add, (mul_comm m q), mul_assoc], 1682 | have h5: mod ((a*q)*m + a*(mod b m)) m = mod (a*(mod b m)) m, from mod_rem (a*q) m (a*(mod b m)), 1683 | have h6: mod (a*b) m = mod (a*(mod b m)) m, by rw [h3, h4, h5], 1684 | h6.symm) 1685 | 1686 | theorem mod_lmult (a b m: ℕ): mod ((mod a m) * b) m = mod (a*b) m := 1687 | by rw [(mul_comm (mod a m) b), mod_rmult, (mul_comm b a)] 1688 | 1689 | theorem right_inv (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : 1690 | ∃ y: ℕ, y ∈ prange p ∧ mod (x*y) p = 1 := 1691 | have h3: coprime x p, from prange_coprime x p h1 h2, 1692 | have h4: x > 0, from prange_pos x p h2, 1693 | have h5: p > 0, from prime_pos p h1, 1694 | have h6: 1 ∈ linear_combo x p, from bezout x p h4 h5 h3, 1695 | exists.elim h6 1696 | (assume y, 1697 | assume: ∃ m:ℕ, x*y = p*m + 1, 1698 | exists.elim this 1699 | (assume m, 1700 | assume h7: x*y = p*m + 1, 1701 | have h8: mod (m*p + 1) p = mod 1 p, from mod_rem m p 1, 1702 | have h9: mod 1 p = 1, from mod_base 1 p h1.left, 1703 | have h10: mod (m*p + 1) p = 1, by rw [h8, h9], 1704 | have h11: m*p = p*m, from mul_comm m p, 1705 | have h12: x*y = m*p + 1, from eq.subst h11.symm h7, 1706 | have h13: mod (x*y) p = 1, from eq.subst h12.symm h10, 1707 | have h14: mod (x * (mod y p)) p = mod (x*y) p, from mod_rmult x y p, 1708 | have h15: mod (x * (mod y p)) p = 1, from eq.subst h14.symm h13, 1709 | have h16: mod y p = 0 ∨ mod y p ≠ 0, from em(mod y p = 0), 1710 | or.elim h16 1711 | (assume h17: mod y p = 0, 1712 | have h18: x * 0 = 0, from rfl, 1713 | have h19: x * mod y p = 0, from eq.subst h17.symm h18, 1714 | have h20: mod 0 p = 1, from eq.subst h19 h15, 1715 | have h21: mod 0 p = 0, from zero_mod p, 1716 | have h22: 0 = 1, by rw [h21.symm, h20], 1717 | absurd h22 zero_ne_one) 1718 | (assume h23: mod y p ≠ 0, 1719 | have h24: mod y p ∈ range p, from mod_less y p h5, 1720 | have h25: mod y p ∈ prange p, from and.intro h24 h23, 1721 | exists.intro (mod y p) (and.intro h25 h15)))) 1722 | 1723 | theorem left_inv (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : 1724 | ∃ y: ℕ, y ∈ prange p ∧ mod (y*x) p = 1 := 1725 | exists.elim (right_inv x p h1 h2) 1726 | (assume y, 1727 | assume h3: y ∈ prange p ∧ mod (x*y) p = 1, 1728 | have h4: x*y = y*x, from mul_comm x y, 1729 | exists.intro y (eq.subst h4 h3)) 1730 | 1731 | lemma smm_eq_1 (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : 1732 | set_mod_mult (prange p) x p ⊆ prange p := 1733 | assume z, 1734 | assume h3: z ∈ set_mod_mult (prange p) x p, 1735 | exists.elim h3 1736 | (assume y, 1737 | assume h4: y ∈ (prange p) ∧ mod (x*y) p = z, 1738 | have h5: mod (x*y) p ∈ prange p, from prange_closed x y p h1 h2 h4.left, 1739 | eq.subst h4.right h5) 1740 | 1741 | lemma smm_eq_2 (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : 1742 | prange p ⊆ set_mod_mult (prange p) x p := 1743 | assume y, 1744 | assume h3: y ∈ prange p, 1745 | have h4: ∃ z: ℕ, z ∈ prange p ∧ mod (z*x) p = 1, from left_inv x p h1 h2, 1746 | exists.elim h4 1747 | (assume z, 1748 | assume h5: z ∈ prange p ∧ mod (z*x) p = 1, 1749 | have h6: mod ((mod (z*x) p) * y) p = mod ((z*x)*y) p, from mod_lmult (z*x) y p, 1750 | have h7: mod (1*y) p = mod ((z*x)*y) p, from eq.subst h5.right h6, 1751 | have h8: mod (1*y) p = mod y p, by rw [(one_mul y)], 1752 | have h9: mod y p = y, from mod_base y p h3.left, 1753 | have h10: mod ((z*x)*y) p = mod (x*(z*y)) p, by rw [(mul_comm z x), (mul_assoc x z y)], 1754 | have h11: mod (z*y) p ∈ prange p, from prange_closed z y p h1 h5.left h3, 1755 | have h12: mod (x * (mod (z*y) p)) p = mod (x*(z*y)) p, from mod_rmult x (z*y) p, 1756 | have h13: mod (x * (mod (z*y) p)) p = y, by rw [h12, h10.symm, h7.symm, h8, h9], 1757 | have h14: mod (z*y) p ∈ prange p ∧ mod (x * (mod (z*y) p)) p = y, from and.intro h11 h13, 1758 | exists.intro (mod (z*y) p) h14) 1759 | 1760 | theorem smm_eq (x p: ℕ) (h1: is_prime p) (h2: x ∈ prange p) : 1761 | set_mod_mult (prange p) x p = prange p := 1762 | set.subset.antisymm (smm_eq_1 x p h1 h2) (smm_eq_2 x p h1 h2) 1763 | 1764 | /- (fprod n f) is the product of f(x) from 1 to n -/ 1765 | def fprod: ℕ → (ℕ → ℕ) → ℕ 1766 | | 0 f := 1 1767 | | (x+1) f := (f (x+1)) * (fprod x f) 1768 | 1769 | lemma pp_base (f: ℕ → ℕ) : fprod 0 f = 1 := rfl 1770 | 1771 | def mulf: (ℕ → ℕ) → (ℕ → ℕ) → ℕ → ℕ 1772 | | f g x := (f x) * (g x) 1773 | 1774 | lemma pp_comm_mult_zero (f g: ℕ → ℕ) : 1775 | (fprod 0 f) * (fprod 0 g) = fprod 0 (mulf f g) := 1776 | have h1: 1 * 1 = 1, from rfl, 1777 | by rw [(pp_base f), (pp_base g), h1, (pp_base (mulf f g)).symm] 1778 | 1779 | theorem pp_comm_mult (n: ℕ) (f g: ℕ → ℕ) : 1780 | (fprod n f) * (fprod n g) = fprod n (mulf f g) := 1781 | nat.rec_on n 1782 | (pp_comm_mult_zero f g) 1783 | (assume y, 1784 | assume h1: (fprod y f) * (fprod y g) = fprod y (mulf f g), 1785 | have h2: fprod (y+1) (mulf f g) = (mulf f g (y+1)) * (fprod y (mulf f g)), from rfl, 1786 | have h3: mulf f g (y+1) = (f (y+1)) * (g (y+1)), from rfl, 1787 | have h4: fprod (y+1) (mulf f g) = ((f (y+1)) * (fprod y f)) * ((g (y+1)) * (fprod y g)), 1788 | by rw [h2, h1.symm, h3, mul_mul_mul_comm], 1789 | show (fprod (y+1) f) * (fprod (y+1) g) = fprod (y+1) (mulf f g), from h4.symm) 1790 | 1791 | def modf: (ℕ → ℕ) → ℕ → ℕ → ℕ 1792 | | f m x := mod (f x) m 1793 | 1794 | lemma pp_comm_mod_zero (m: ℕ) (f: ℕ → ℕ) : mod (fprod 0 (modf f m)) m = mod (fprod 0 f) m := 1795 | have h1: fprod 0 (modf f m) = 1, from rfl, 1796 | have h2: fprod 0 f = 1, from rfl, 1797 | by rw [h1, h2.symm] 1798 | 1799 | theorem pp_comm_mod (m n: ℕ) (f: ℕ → ℕ) : 1800 | mod (fprod n (modf f m)) m = mod (fprod n f) m := 1801 | nat.rec_on n 1802 | (pp_comm_mod_zero m f) 1803 | (assume x, 1804 | assume h1: mod (fprod x (modf f m)) m = mod (fprod x f) m, 1805 | have h2: fprod (x+1) (modf f m) = (modf f m (x+1)) * (fprod x (modf f m)), from rfl, 1806 | have h3: mod (fprod (x+1) (modf f m)) m = mod ((modf f m (x+1)) * mod (fprod x (modf f m)) m) m, 1807 | by rw [h2, mod_rmult], 1808 | have h4: modf f m (x+1) = mod (f (x+1)) m, from rfl, 1809 | have h5: mod (fprod (x+1) (modf f m)) m = mod ((f (x+1)) * (fprod x f)) m, 1810 | by rw [h3, h1, mod_rmult, h4, mod_lmult], 1811 | have h6: (f (x+1)) * (fprod x f) = fprod (x+1) f, from rfl, 1812 | show mod (fprod (x+1) (modf f m)) m = mod (fprod (x+1) f) m, from eq.subst h6 h5) 1813 | 1814 | def constf: ℕ → ℕ → ℕ 1815 | | a b := a 1816 | 1817 | def exp: ℕ → ℕ → ℕ 1818 | | a b := fprod b (constf a) 1819 | 1820 | def prangemap: ℕ → (ℕ → ℕ) → (set ℕ) 1821 | | n f := {b: ℕ | ∃ a: ℕ, a ∈ prange n ∧ f a = b} 1822 | 1823 | theorem flt (a p: ℕ) (h1: is_prime p) (h2: a ∈ prange p) : mod (exp a (p-1)) p = 1 := sorry 1824 | 1825 | /- 1826 | 1827 | TODO: Fermat's Little Theorem. 1828 | 1829 | We need to prove that (p-1)! is equal, no matter what order we multiply it in. 1830 | Maybe we can do this with prangemaps - can we prove that if two functions have equal prangemaps, 1831 | their fprods are the same? 1832 | 1833 | We need to calculate (p-1)! two ways, before and after multiplying by a. 1834 | They're equal mod p, because it's the same set of numbers. 1835 | 1836 | I should also check out the community. If there's a future, it's in there somewhere. 1837 | -/ 1838 | 1839 | -------------------------------------------------------------------------------- /lean4/division.lean: -------------------------------------------------------------------------------- 1 | open Classical 2 | 3 | inductive Cnat where 4 | | zero : Cnat 5 | | succ : Cnat → Cnat 6 | 7 | def add (a b : Cnat) : Cnat := 8 | match a with 9 | | Cnat.zero => b 10 | | Cnat.succ c => Cnat.succ (add c b) 11 | 12 | theorem add_zero_right (x : Cnat) : add x Cnat.zero = x := by 13 | induction x 14 | case zero => rfl 15 | case succ x' ih => simp [ih, add] 16 | 17 | theorem add_suc_right (x y : Cnat) : add x (Cnat.succ y) = Cnat.succ (add x y) := by 18 | induction x 19 | case zero => rfl 20 | case succ x' ih => simp [ih, add] 21 | 22 | theorem add_comm (x y : Cnat) : add x y = add y x := by 23 | induction x 24 | case zero => simp [add, add_zero_right] 25 | case succ x' ih => simp [ih, add, add_suc_right] 26 | 27 | theorem add_assoc (x y z : Cnat) : add (add x y) z = add x (add y z) := by 28 | induction x 29 | case zero => simp [add] 30 | case succ x' ih => simp [ih, add] 31 | 32 | def mul (a b : Cnat) : Cnat := 33 | match a with 34 | | Cnat.zero => Cnat.zero 35 | | Cnat.succ c => add b (mul c b) 36 | 37 | theorem mul_zero_right (x : Cnat) : mul x Cnat.zero = Cnat.zero := by 38 | induction x 39 | case zero => rfl 40 | case succ x' ih => simp [ih, mul, add] 41 | 42 | theorem mul_suc_right (x y : Cnat) : mul x (Cnat.succ y) = add x (mul x y) := by 43 | induction x 44 | case zero => simp [mul, add] 45 | case succ x' ih => simp [mul, add, ih, <-add_assoc, add_comm] 46 | 47 | theorem mul_comm (x y : Cnat) : mul x y = mul y x := by 48 | induction x 49 | case zero => simp [mul, mul_zero_right] 50 | case succ x' ih => simp [mul, mul_suc_right, ih] 51 | 52 | theorem distrib (x y z : Cnat) : mul x (add y z) = add (mul x y) (mul x z) := by 53 | induction x 54 | case zero => simp [mul, add] 55 | case succ x' ih => rw [mul, mul, mul, add_assoc, ih, add_assoc, 56 | <- add_assoc (mul x' y), <- add_assoc z, add_comm z] 57 | 58 | theorem mul_assoc (x y z : Cnat) : mul (mul x y) z = mul x (mul y z) := by 59 | induction x 60 | case zero => simp [mul] 61 | case succ x' ih => simp [mul, mul_comm, distrib, <- ih] 62 | 63 | def lt (a b : Cnat) : Prop := 64 | match b with 65 | | Cnat.zero => False 66 | | Cnat.succ d => match a with 67 | | Cnat.zero => True 68 | | Cnat.succ c => lt c d 69 | 70 | theorem lt_not_ref (x : Cnat) : ¬ lt x x := by 71 | induction x 72 | case zero => simp [lt] 73 | case succ x' ih => simp [lt, ih] 74 | 75 | theorem lt_not_symm (a b : Cnat) : lt a b → ¬ lt b a := by 76 | induction a generalizing b 77 | case zero => simp [lt] 78 | case succ a' ih => match b with 79 | | Cnat.zero => simp [lt] 80 | | Cnat.succ b' => simp [lt]; apply ih 81 | 82 | theorem lt_trans (a b c : Cnat) : lt a b ∧ lt b c → lt a c := by 83 | induction c generalizing a b 84 | case zero => simp [lt] 85 | case succ c' ih => match a with 86 | | Cnat.zero => simp [lt] 87 | | Cnat.succ a' => match b with 88 | | Cnat.zero => simp [lt] 89 | | Cnat.succ b' => simp [lt]; apply ih 90 | 91 | theorem lt_to_sub (a b : Cnat) : lt a b → ∃ c, b = add a c := by 92 | induction a generalizing b 93 | case zero => simp [add]; intro; apply Exists.intro b; rfl 94 | case succ a ih => 95 | simp [add] 96 | match b with 97 | | Cnat.zero => simp [lt] 98 | | Cnat.succ b' => simp [lt]; apply ih 99 | 100 | theorem lt_add_suc (a b : Cnat) : lt a (add a b.succ) := by 101 | induction a generalizing b 102 | case zero => simp [add, lt] 103 | case succ a ih => simp [add, lt]; apply ih 104 | 105 | theorem add_cancels_left (a b c : Cnat) : add a b = add a c → b = c := by 106 | induction a generalizing b 107 | case zero => simp [add]; intro h; exact h 108 | case succ a ih => simp [add]; apply ih 109 | 110 | theorem add_cancels_right (a b c : Cnat) : add a c = add b c → a = b := by 111 | induction c 112 | case zero => simp [add_zero_right]; intro h; exact h 113 | case succ c ih => simp [add_suc_right]; exact ih 114 | 115 | theorem lt_suc (a b : Cnat) (h1: lt a b) : a.succ = b ∨ lt a.succ b := by 116 | let ⟨c, h2⟩ := lt_to_sub a b h1 117 | match c with 118 | | Cnat.zero => { 119 | simp [add_zero_right] at h2; rw [h2] at h1; simp [lt_not_ref] at h1 120 | } 121 | | Cnat.succ c' => match c' with 122 | | Cnat.zero => { 123 | simp [add_suc_right, add_zero_right] at h2 124 | simp [h2] 125 | } 126 | | Cnat.succ c'' => { 127 | apply Or.inr 128 | rw [add_suc_right] at h2 129 | rw [h2] 130 | simp [lt] 131 | exact lt_add_suc _ _ 132 | } 133 | 134 | theorem division_theorem (m n : Cnat) (h1: lt Cnat.zero n) : 135 | ∃ q r, lt r n ∧ m = add (mul q n) r := by 136 | induction m 137 | case zero => { 138 | apply Exists.intro Cnat.zero 139 | apply Exists.intro Cnat.zero 140 | simp [h1, mul, add] 141 | } 142 | case succ m ih => { 143 | let ⟨q, r, h2, h3⟩ := ih 144 | apply Or.elim (em (r.succ = n)) 145 | case left => { 146 | intro h4 147 | apply Exists.intro q.succ 148 | apply Exists.intro Cnat.zero 149 | simp [h1, add_zero_right, h3, mul] 150 | rw [<- h4, add, add_comm] 151 | } 152 | case right => { 153 | intro h5 154 | apply Exists.intro q 155 | apply Exists.intro r.succ 156 | apply And.intro 157 | case left => { 158 | apply Or.elim (lt_suc r n h2) 159 | case left => simp [h5] 160 | case right => intro h6; exact h6 161 | } 162 | case right => { 163 | rw [h3] 164 | simp [add_suc_right] 165 | } 166 | } 167 | } 168 | 169 | def is_prime (p : Cnat) : Prop := sorry 170 | -------------------------------------------------------------------------------- /ocaml/hello.ml: -------------------------------------------------------------------------------- 1 | let () = print_endline "Hello, World!" 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kata", 3 | "version": "0.0.1", 4 | "description": "If you want to get better at something, repeating practice alone is not enough. You must practice with increased difficulty and challenge.", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/lacker/kata.git" 12 | }, 13 | "author": "", 14 | "license": "UNLICENSED", 15 | "bugs": { 16 | "url": "https://github.com/lacker/kata/issues" 17 | }, 18 | "homepage": "https://github.com/lacker/kata#readme", 19 | "devDependencies": { 20 | "jest": "^19.0.2" 21 | }, 22 | "dependencies": { 23 | "collections": "^5.0.6" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /python/algebra.py: -------------------------------------------------------------------------------- 1 | class Term(object): 2 | def __init__(self): 3 | pass 4 | 5 | def map(self, mapping): 6 | return self 7 | 8 | def is_constant(self): 9 | return False 10 | 11 | def is_variable(self): 12 | return False 13 | 14 | def is_composite(self): 15 | if self.is_variable(): 16 | return False 17 | return not self.is_constant() 18 | 19 | class Constant(Term): 20 | def __init__(self, token): 21 | self.token = token 22 | 23 | def __eq__(self, other): 24 | return self.token == other.token 25 | 26 | def __str__(self): 27 | return str(self.token) 28 | 29 | def is_constant(self): 30 | return True 31 | 32 | class Variable(Term): 33 | def __init__(self, number): 34 | self.number = number 35 | 36 | def map(self, mapping): 37 | return mapping.get(self.number, self) 38 | 39 | def is_variable(self): 40 | return True 41 | 42 | def __str__(self): 43 | return f"x{number}" 44 | 45 | class Composite(Term): 46 | def __init__(self, head, args): 47 | self.head = head 48 | self.args = args 49 | 50 | def __str__(self): 51 | arg_str = " ".join(map(str, self.args)) 52 | return f"({head} {args})" 53 | 54 | def map(self, mapping): 55 | head = self.head.map(mapping) 56 | args = [arg.map(mapping) for arg in self.args] 57 | 58 | # Modifies the maps 59 | def unify_var(n, term, var_map, term_map): 60 | existing = term_map.get(number) 61 | comp = var_map.get(number) 62 | if existing is not None: 63 | if existing == comp: 64 | return 65 | raise ValueError("cannot unify") 66 | term_map[number] = comp 67 | 68 | def unify(left, right, left_map, right_map): 69 | if left.is_variable(): 70 | unify_var(left.number, right, left_map, right_map) 71 | return 72 | if right.is_variable(): 73 | unify_var(right.number, left, right_map, left_map) 74 | return 75 | if left == right: 76 | return 77 | if not left.is_composite(): 78 | raise ValueError("cannot unify") 79 | if not right.is_composite(): 80 | raise ValueError("cannot unify") 81 | if len(left.args) != len(right.args): 82 | raise ValueError("arg len mismatch") 83 | unify(left.head, right.head, left_map, right_map) 84 | for left_arg, right_arg in zip(left.args, right.args): 85 | unify(left_arg, right_arg, left_map, right_map) 86 | 87 | def split_on_char(s, ch): 88 | parts = [] 89 | for i, part in enumerate(s.split(ch)): 90 | if i > 0: 91 | parts.append(ch) 92 | parts.append(part) 93 | return parts 94 | 95 | def split_on_chars(s, chars): 96 | answer = [s] 97 | for ch in chars: 98 | new_answer = [] 99 | for part in answer: 100 | new_parts = split_on_char(s, part) 101 | new_answer += new_parts 102 | answer = new_answer 103 | return answer 104 | 105 | def sparse(string): 106 | """ 107 | Parse an s expression into lists. 108 | """ 109 | tokens = split_on_chars(string, "() ") 110 | answer, rest = nest(tokens) 111 | assert not rest 112 | return answer 113 | 114 | def nest(tokens): 115 | """ 116 | Turn the first parenthesized section 117 | into nested lists. 118 | Return a tuple of the nested 119 | section, then the rest of the tokens. 120 | """ 121 | assert tokens 122 | first = tokens[0] 123 | rest = tokens[1:] 124 | if first == ")": 125 | raise ValueError("bad )") 126 | if first != "(": 127 | return first, rest 128 | if not rest or rest[-1] != ")": 129 | raise ValueError("missing )") 130 | 131 | # The whole thing is parenthesized 132 | pending = rest[:-1] 133 | answer = [] 134 | while pending: 135 | item, pending = nest(pending) 136 | answer.append(item) 137 | return answer 138 | 139 | def parse(string): 140 | "Parse an s expression into a Term." 141 | lists = sparse(string) 142 | return make_term(lists) 143 | 144 | def make_term(expr): 145 | if type(expr) is list: 146 | if not(expr): 147 | raise ValueError("empty list") 148 | subs = [make_term(sub) for sub in expr] 149 | head = subs[0] 150 | args = subs[1:] 151 | return Composite(head, args) 152 | if type(expr) is str: 153 | if not expr: 154 | raise ValueError("empty string") 155 | if expr.startswith("x"): 156 | num_str = expr[1:] 157 | number = int(num_str) 158 | return Variable(number) 159 | if expr.startswith("c"): 160 | num_str = expr[1:] 161 | number = int(num_str) 162 | return Constant(number) 163 | raise ValueError(f"unexpected expr: {expr}") 164 | raise ValueError(f"unexpected expr: {repr(expr)}") 165 | 166 | def check(s): 167 | term = make_term(s) 168 | output = str(term) 169 | assert s == output 170 | 171 | def test(): 172 | check("x2") 173 | 174 | test() -------------------------------------------------------------------------------- /python/almost_palindrome.py: -------------------------------------------------------------------------------- 1 | # whether s from i to j inclusive is a palindrome 2 | def seq_is_pal(s, i, j): 3 | if i >= j: 4 | return True 5 | if s[i] != s[j]: 6 | return False 7 | return seq_is_pal(s, i+1, j-1) 8 | 9 | def is_almost_pal(s, i, j): 10 | if i >= j: 11 | return True 12 | if s[i] == s[j]: 13 | return is_almost_pal(s, i+1, j-1) 14 | 15 | return "xxx" 16 | 17 | def is_pal(s): 18 | return seq_is_pal(s, 0, len(s)-1) 19 | 20 | print(is_pal("foof")) 21 | print(is_pal("boof")) 22 | -------------------------------------------------------------------------------- /python/chess1d.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | One-dimensional chess. See https://gumroad.com/l/1DChess 4 | """ 5 | 6 | import random 7 | import sys 8 | import time 9 | 10 | START = "KQRBNP....pnbrqk" 11 | 12 | WHITE = "white" 13 | BLACK = "black" 14 | 15 | def get_color(char): 16 | if "A" <= char <= "Z": 17 | return WHITE 18 | if "a" <= char <= "z": 19 | return BLACK 20 | if char == ".": 21 | return None 22 | 23 | def opposite_color(c): 24 | if c == WHITE: 25 | return BLACK 26 | elif c == BLACK: 27 | return WHITE 28 | else: 29 | raise RuntimeError("oc of " + str(c)) 30 | 31 | def empty(board, i): 32 | if i < 0: 33 | return False 34 | if i >= len(board): 35 | return False 36 | return board[i] == "." 37 | 38 | def movable(board, color, i): 39 | if i < 0 or i >= len(board): 40 | return False 41 | if empty(board, i): 42 | return True 43 | return get_color(board[i]) == opposite_color(color) 44 | 45 | def step_helper(board, color, i, steps): 46 | answer = [] 47 | for step in steps: 48 | delta = step 49 | while empty(board, i + delta): 50 | answer.append((i, i + delta)) 51 | delta += step 52 | if movable(board, color, i + delta): 53 | answer.append((i, i + delta)) 54 | return answer 55 | 56 | def legal_moves(board, color): 57 | """ 58 | Moves are represented as a (i, j) tuple, where the piece is moving from board[i] to board[j]. 59 | """ 60 | if winner(board): 61 | return [] 62 | moves = [] 63 | for i, piece in enumerate(board): 64 | if get_color(board[i]) != color: 65 | continue 66 | if piece == "P": 67 | if movable(board, WHITE, i+1): 68 | moves.append((i, i+1)) 69 | if i == 5 and empty(board, 6) and empty(board, 7): 70 | moves.append((5, 7)) 71 | elif piece == "p": 72 | if movable(board, BLACK, i-1): 73 | moves.append((i, i-1)) 74 | if i == 10 and empty(board, 9) and empty(board, 8): 75 | moves.append((10, 8)) 76 | elif piece in "Nn": 77 | for delta in (-3, -2, 2, 3): 78 | if movable(board, color, i + delta): 79 | moves.append((i, i + delta)) 80 | elif piece in "Bb": 81 | moves += step_helper(board, color, i, (-2, 2)) 82 | elif piece in "Rr": 83 | moves += step_helper(board, color, i, (-1, 1)) 84 | elif piece in "Qq": 85 | moves += step_helper(board, color, i, (-2, -1, 1, 2)) 86 | elif piece in "Kk": 87 | for delta in (-1, 1): 88 | if movable(board, color, i + delta): 89 | moves.append((i, i + delta)) 90 | return moves 91 | 92 | def make_move(board, move): 93 | pre, post = move 94 | new_board = list(board) 95 | new_board[post] = new_board[pre] 96 | new_board[pre] = "." 97 | return "".join(new_board) 98 | 99 | def is_capture(board, move): 100 | pre, post = move 101 | return board[post] != "." 102 | 103 | def invert(board): 104 | chars = list(board) 105 | inverted_chars = [] 106 | for char in chars: 107 | if char.islower(): 108 | inverted_chars.append(char.upper()) 109 | elif char.isupper(): 110 | inverted_chars.append(char.lower()) 111 | else: 112 | inverted_chars.append(char) 113 | return "".join(reversed(inverted_chars)) 114 | 115 | def winner(board): 116 | if "K" not in board: 117 | return BLACK 118 | if "k" not in board: 119 | return WHITE 120 | return None 121 | 122 | MAX_SCORE = 2000 123 | 124 | SCORE_MAP = { 125 | "K": 100, 126 | "k": -100, 127 | "Q": 9, 128 | "q": -9, 129 | "R": 5, 130 | "r": -5, 131 | "B": 3, 132 | "b": -3, 133 | "N": 3, 134 | "n": -3, 135 | "P": 1, 136 | "p": -1, 137 | ".": 0, 138 | } 139 | 140 | def get_score(board, player, to_move): 141 | """ 142 | Returns a score for the given player. 143 | """ 144 | # classic chess material, knight = 3 etc 145 | material = 0 146 | # non-king pieces total, to see if it's endgame 147 | nonking = 0 148 | 149 | for ch in board: 150 | material += SCORE_MAP[ch] 151 | if ch not in ".kK": 152 | nonking += 1 153 | 154 | if nonking == 0: 155 | # endgame 156 | wk_pos = board.index("K") 157 | bk_pos = board.index("k") 158 | diff = abs(wk_pos - bk_pos) 159 | zugzwang = (diff % 2 == 0) 160 | # incentivize forcing the win 161 | value = 900 - 10 * diff 162 | if zugzwang == (player == to_move): 163 | # we lose in zugzwang 164 | return -value 165 | else: 166 | return value 167 | 168 | 169 | if player == WHITE: 170 | return material 171 | if player == BLACK: 172 | return -material 173 | raise ValueError("bad player value") 174 | 175 | def invert_move(move): 176 | if move is None: 177 | return None 178 | return tuple(len(START) - 1 - i for i in move) 179 | 180 | def tree_search(board, player, alpha=MAX_SCORE, beta=-MAX_SCORE, depth=4, cache=None): 181 | """ 182 | Return (score, move, positions searched) for the player to move. 183 | Positive scores are better. 184 | Score can be truncated to the (alpha, beta) range. 185 | If we can't find any move that even achieves alpha, return (alpha, None). 186 | cache maps (board, player) to a score when we have exhausted the game tree. 187 | """ 188 | if cache is None: 189 | cache = {} 190 | key = (board, player) 191 | if key in cache: 192 | b, p = cache[key] 193 | return b, p, 1 194 | 195 | w = winner(board) 196 | if w: 197 | if w == player: 198 | return 1000, None, 1 199 | return -1000, None, 1 200 | 201 | if depth <= 0: 202 | s = get_score(board, player, player) 203 | return s, None, 1 204 | subdepth = depth - 1 205 | 206 | best_score = alpha 207 | best_moves = [] 208 | legal = legal_moves(board, player) 209 | position_count = 1 210 | 211 | for move in legal: 212 | new_board = make_move(board, move) 213 | 214 | subscore, submove, subcount = tree_search(new_board, opposite_color(player), -beta, -alpha, depth=subdepth) 215 | possible_score = -subscore 216 | position_count += subcount 217 | 218 | # Incentivize not stalling forever 219 | if possible_score > 900: 220 | possible_score -= 1 221 | 222 | if possible_score >= beta: 223 | if abs(possible_score) > 100: 224 | cache[key] = possible_score, move 225 | return possible_score, move, position_count 226 | 227 | if possible_score > best_score: 228 | best_score = possible_score 229 | best_moves = [move] 230 | elif possible_score == best_score: 231 | best_moves.append(move) 232 | 233 | if not best_moves: 234 | return alpha, None, position_count 235 | 236 | return best_score, random.choice(best_moves), position_count 237 | 238 | 239 | def deepen_search(board, turn): 240 | depth = 1 241 | start = time.time() 242 | while True: 243 | score, move, count = tree_search(board, turn, depth=depth) 244 | if abs(score) > 100: 245 | print("hit bottom at depth", depth) 246 | print("searched", count, "positions") 247 | return score, move, count 248 | elapsed = time.time() - start 249 | if elapsed > 5: 250 | return score, move, count 251 | depth += 1 252 | 253 | 254 | def play_game(): 255 | board = START 256 | # todo: use n positions not depth 257 | depth = 4 258 | turn = WHITE 259 | print() 260 | while True: 261 | score, move, count = deepen_search(board, turn) 262 | print(f"{turn} score:", score, "move:", move) 263 | if move is None: 264 | print(f"{turn} resigns") 265 | return opposite_color(turn) 266 | board = make_move(board, move) 267 | print("board:", board) 268 | if winner(board): 269 | print(f"{turn} wins") 270 | return turn 271 | turn = opposite_color(turn) 272 | 273 | 274 | def test(): 275 | # Black to move should win 276 | board = "KQ...n...B..brqk" 277 | print("starting board:", board) 278 | legal = legal_moves(board, BLACK) 279 | # print("legal:", legal) 280 | for move in legal: 281 | new_board = make_move(board, move) 282 | score = get_score(new_board, BLACK) 283 | print(move, score) 284 | 285 | score, move = tree_search(board, 1, BLACK) 286 | print("best move:", move, "with score", score) 287 | 288 | def test2(): 289 | board = "K........R.P.rk." 290 | print("starting board:", board) 291 | 292 | play_game() 293 | # test() 294 | 295 | -------------------------------------------------------------------------------- /python/drill.py: -------------------------------------------------------------------------------- 1 | vars = ["a", "b", "c"] 2 | 3 | class Op: 4 | pass -------------------------------------------------------------------------------- /python/gen_problems.py: -------------------------------------------------------------------------------- 1 | MAX = 10000 2 | 3 | def generate_str_pair(): 4 | left = random.randrange(0, MAX) 5 | right = random.randrange(0, MAX) 6 | symbol = "+" 7 | answer = str(left + right) 8 | s = f"{left} {symbol} {right}" 9 | return (s, answer) 10 | 11 | class Tokenizer: 12 | def __init__(self): 13 | self.n_to_ch = [] 14 | self.ch_to_n = {} 15 | 16 | def __len__(self): 17 | len1 = len(self.n_to_ch) 18 | len2 = len(self.ch_to_n) 19 | assert len1 == len2 20 | return len1 21 | 22 | def to_token(self, ch): 23 | if ch in self.ch_to_n: 24 | return self.ch_to_n[ch] 25 | n = len(self.ch_to_n) 26 | self.ch_to_n[ch] = n 27 | return n 28 | 29 | def to_char(self, n): 30 | return self.n_to_ch[n] 31 | 32 | def generate_token_pair(): 33 | in_str, out_str = generate_str_pair() 34 | in_tokens = [self.to_token(ch) for ch in in_str] 35 | out_tokens = [self.to_token(ch) for ch in out_str] 36 | return in_tokens, out_tokens 37 | 38 | def main(): 39 | print(generate_str_pair()) 40 | 41 | -------------------------------------------------------------------------------- /python/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | print("hello world") 4 | -------------------------------------------------------------------------------- /python/lisp.py: -------------------------------------------------------------------------------- 1 | ENV = {} 2 | 3 | def macro(f): 4 | f.macro = True 5 | return f 6 | 7 | def expose(f): 8 | ENV[f.__name__] = f 9 | return f 10 | 11 | @macro 12 | def define(symbol, expr): 13 | ENV[symbol] = expr 14 | 15 | @macro 16 | def quote(x): 17 | return x 18 | 19 | def is_macro(f): 20 | try: 21 | f.macro 22 | except AttributeError: 23 | return False 24 | return True 25 | 26 | def evaluate(expr, local={}): 27 | t = type(expr) 28 | if t in (int, float): 29 | return expr 30 | if t is str: 31 | return evaluate(ENV[expr], local=local) 32 | if callable(expr): 33 | return expr 34 | if t is not list: 35 | raise BaseException(f"cannot evaluate type: {t}") 36 | 37 | op = evaluate(expr[0]) 38 | args = map(evaluate, expr[1:]) 39 | if not callable(op): 40 | raise BaseException(f"{op} is not callable") 41 | if is_macro(op): 42 | return op(*args) 43 | args = map(evaluate, args) 44 | return op(*args) 45 | 46 | def let(varname, value, expr, local={}): 47 | new_local = dict(local) 48 | new_local[varname] = value 49 | return evaluate(expr, local=new_local) 50 | 51 | @expose 52 | def add(*args): 53 | answer = 0 54 | for arg in args: 55 | answer += arg 56 | return answer 57 | 58 | @expose 59 | def mul(*args): 60 | answer = 1 61 | for arg in args: 62 | answer *= arg 63 | return answer 64 | 65 | @expose 66 | def cons(first, rest): 67 | answer = [first] 68 | answer.extend(rest) 69 | return answer 70 | 71 | @expose 72 | def car(x): 73 | return x[0] 74 | 75 | @expose 76 | def cdr(x): 77 | return x[1:] 78 | 79 | @expose 80 | def xmap(f, items): 81 | return list(map(f, items)) 82 | 83 | @expose 84 | def xfilter(f, items): 85 | return list(filter(f, items)) 86 | 87 | def monolamb(expr): 88 | raise Exception("TODO") 89 | 90 | def seq(exprs): 91 | res = None 92 | for expr in exprs: 93 | res = evaluate(expr) 94 | return res 95 | 96 | assert evaluate(1) == 1 97 | assert evaluate(1.25) == 1.25 98 | assert evaluate([add, 1, 2, 3]) == 6 99 | assert evaluate(["add", 1, 2, 3]) == 6 100 | print("ok") 101 | -------------------------------------------------------------------------------- /python/mastermind.py: -------------------------------------------------------------------------------- 1 | from random import choice, shuffle 2 | 3 | SIZE = 6 4 | DIGITS = range(SIZE) 5 | 6 | def generate(): 7 | answer = [] 8 | for _ in range(SIZE): 9 | answer.append(choice(DIGITS)) 10 | return answer 11 | 12 | def all_possible(length): 13 | if length == 0: 14 | return [[]] 15 | answer = [] 16 | tails = all_possible(length - 1) 17 | for first in DIGITS: 18 | for tail in tails: 19 | answer.append([first] + tail) 20 | return answer 21 | 22 | def all_perms(digits): 23 | if not digits: 24 | return [[]] 25 | subperms = all_perms(digits[1:]) 26 | item = digits[0] 27 | answer = [] 28 | for subperm in subperms: 29 | for i in range(len(digits)): 30 | # put item at spot i 31 | pre = subperm[:i] 32 | post = subperm[i:] 33 | newperm = pre + [item] + post 34 | answer.append(newperm) 35 | return answer 36 | 37 | def right_place(perm1, perm2): 38 | score = 0 39 | for x, y in zip(perm1, perm2): 40 | if x == y: 41 | score += 1 42 | return score 43 | 44 | def make_count(items): 45 | answer = {} 46 | for item in items: 47 | answer[item] = answer.get(item, 0) + 1 48 | return answer 49 | 50 | def right_items(list1, list2): 51 | count1 = make_count(list1) 52 | count2 = make_count(list2) 53 | answer = 0 54 | for (item, count) in count1.items(): 55 | answer += min(count, count2.get(item, 0)) 56 | return answer 57 | 58 | def right_answer(list1, list2): 59 | s1 = sorted(list1) 60 | s2 = sorted(list2) 61 | return s1 == s2 62 | 63 | def info(list1, list2): 64 | return ( 65 | right_answer(list1, list2), 66 | right_items(list1, list2), 67 | right_place(list1, list2)) 68 | 69 | # returns whether you did it 70 | def check(list1, list2): 71 | ra, ri, rp = info(list1, list2) 72 | if ra: 73 | # print("correct!") 74 | return True 75 | # print(rp, "in the right place") 76 | # print(ri - rp, "in the wrong place") 77 | return False 78 | 79 | def read(): 80 | while True: 81 | line = input("guess:") 82 | chars = list(line.strip()) 83 | try: 84 | digits = list(map(int, chars)) 85 | if len(digits) != SIZE: 86 | continue 87 | return digits 88 | except: 89 | pass 90 | 91 | class HumanPlayer: 92 | def __init__(self): 93 | pass 94 | 95 | def guess(self): 96 | return read() 97 | 98 | def inform(self, guess, ra, ri, rp): 99 | if ra: 100 | print("correct!") 101 | print(rp, "in the right place") 102 | print(ri - rp, "in the wrong place") 103 | 104 | 105 | class ComputerPlayer(): 106 | def __init__(self): 107 | self.possibilities = all_possible(SIZE) 108 | 109 | def inform(self, guess, ra, ri, rp): 110 | new_possibilities = [] 111 | for possibility in self.possibilities: 112 | ta, ti, tp = info(guess, possibility) 113 | if (ta, ti, tp) == (ra, ri, rp): 114 | new_possibilities.append(possibility) 115 | if not new_possibilities: 116 | raise Exception("no possibilities left") 117 | self.possibilities = new_possibilities 118 | 119 | def guess(self): 120 | answer = self.possibilities[0] 121 | # print("guessing:", answer) 122 | return answer 123 | def play(player_class): 124 | # print("let's play a game") 125 | player = player_class() 126 | guesses = 0 127 | target = generate() 128 | # print("target:", target) 129 | while True: 130 | guess = player.guess() 131 | # print("guess:", guess) 132 | if guess is None: 133 | raise Exception("guess is None") 134 | guesses += 1 135 | if check(target, guess): 136 | # print("you win with", guesses, "guesses") 137 | return guesses, target 138 | ra, ri, rp = info(target, guess) 139 | player.inform(guess, ra, ri, rp) 140 | 141 | def average(player_class, rounds): 142 | total = 0 143 | worst = 0 144 | worst_target = None 145 | played = 0 146 | for _ in range(rounds): 147 | n, target = play(player_class) 148 | total += n 149 | played += 1 150 | worst = max(worst, n) 151 | if worst == n: 152 | worst_target = target 153 | 154 | avg = total / played 155 | if played % 10 == 0: 156 | print(f"{played} runs") 157 | print("average:", avg) 158 | print("worst:", worst) 159 | print() 160 | 161 | average(ComputerPlayer, 1000) 162 | -------------------------------------------------------------------------------- /python/solver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class Expression: 4 | def __str__(self): 5 | pass 6 | 7 | class Equals(Expression): 8 | def __init__(self, left, right): 9 | self.left = left 10 | self.right = right 11 | 12 | def __str__(self): 13 | left = str(self.left) -------------------------------------------------------------------------------- /python/substring_anagrams.py: -------------------------------------------------------------------------------- 1 | # Updates values in place 2 | def update(values, key, delta): 3 | new_value = values.get(key, 0) + delta 4 | if new_value: 5 | values[key] = new_value 6 | else: 7 | del values[key] 8 | 9 | def find_substring_anagrams(big, small): 10 | count = {} 11 | for ch in small: 12 | update(count, ch, -1) 13 | answer = [] 14 | for i, ch in enumerate(big): 15 | update(count, ch, 1) 16 | j = i - len(small) 17 | if j >= 0: 18 | update(count, big[j], -1) 19 | if not count: 20 | answer.append(i - len(small) + 1) 21 | return answer 22 | 23 | find_substring_anagrams("abcdefedcba", "feed") 24 | find_substring_anagrams("","") 25 | -------------------------------------------------------------------------------- /python/unification.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | class Expression: 5 | def __init__(self, token=None, left=None, right=None, variable_id=None): 6 | self.token = token 7 | self.left = left 8 | self.right = right 9 | self.variable_id = variable_id 10 | 11 | self.h = 0 12 | for x in [token, left, right, variable_id]: 13 | self.h = 2 * self.h + hash(x) 14 | 15 | def __str__(self): 16 | if self.variable_id is not None: 17 | return f"V{self.variable_id}" 18 | if self.left is None and self.right is None: 19 | return str(self.token) 20 | return f"({self.left} {self.token} {self.right})" 21 | 22 | def is_variable(self): 23 | return self.variable_id is not None 24 | 25 | def any(self, f): 26 | if f(self): 27 | return True 28 | if self.left and f(self.left): 29 | return True 30 | return self.right and f(self.right) 31 | 32 | def __hash__(self): 33 | return self.h 34 | 35 | def contains(self, h): 36 | return self.any(lambda x: x.h == h) 37 | 38 | def has_var(self, v): 39 | return self.any(lambda x: x.variable_id == v) 40 | 41 | def replace(self, v, expr): 42 | if self.variable_id == v: 43 | return expr 44 | if self.left is None: 45 | new_left = None 46 | else: 47 | new_left = self.left.replace(v, expr) 48 | if self.right is None: 49 | new_right = None 50 | else: 51 | new_right = self.right.replace(v, expr) 52 | return Expression(left=new_left, right=new_right, variable_id=self.variable_id, token=self.token) 53 | 54 | def sub(self, varmap): 55 | answer = self 56 | for v, expr in varmap.items(): 57 | answer = answer.replace(v, expr) 58 | return answer 59 | 60 | def __eq__(self, other): 61 | if self.token != other.token: 62 | return False 63 | if self.left != other.left: 64 | return False 65 | 66 | 67 | def V(id): 68 | return Expression(variable_id=id) 69 | 70 | def C(id): 71 | return Expression(token=id) 72 | 73 | def unify(lhs, rhs): 74 | """ 75 | Returns a tuple of the varmap and the unified expression. 76 | """ 77 | print(f"unifying {lhs} and {rhs}") 78 | if lhs is None: 79 | if rhs is None: 80 | return {}, None 81 | raise ValueError("cannot unify None") 82 | if rhs is None: 83 | raise ValueError("cannot unify None") 84 | 85 | if lhs.h == rhs.h: 86 | return {}, lhs 87 | if lhs.is_variable(): 88 | if rhs.has_var(lhs.variable_id): 89 | raise ValueError("cannot unify with subtree") 90 | answer = {} 91 | answer[lhs.variable_id] = rhs 92 | return answer, rhs 93 | if rhs.is_variable(): 94 | if lhs.has_var(rhs.variable_id): 95 | raise ValueError("cannot unify with subtree") 96 | answer = {} 97 | answer[rhs.variable_id] = lhs 98 | return answer, lhs 99 | 100 | if lhs.token != rhs.token: 101 | raise ValueError(f"token mismatch: {lhs.token} != {rhs.token}") 102 | 103 | lsubs, _ = unify(lhs.left, rhs.left) 104 | print("lsubs:", lsubs) 105 | new_lhs = lhs and lhs.sub(lsubs) 106 | print("new lhs:", new_lhs) 107 | new_rhs = rhs and rhs.sub(lsubs) 108 | print("new rhs:", new_rhs) 109 | 110 | rsubs, final_right = unify(new_lhs.right, new_rhs.right) 111 | final_left = new_lhs.left.sub(rsubs) 112 | 113 | subs = dict(lsubs) 114 | for k, v in rsubs.items(): 115 | # todo: fail on mismatch 116 | subs[k] = v 117 | 118 | return subs, Expression(token=lhs.token, right=final_right, left=final_left) 119 | 120 | def add(left, right): 121 | return Expression(token="+", left=left, right=right) 122 | 123 | def mul(left, right): 124 | return Expression(token="*", left=left, right=right) 125 | 126 | def test1(): 127 | left = add(V(1), C(2)) 128 | right = add(C(1), V(2)) 129 | varmap, expr = unify(left, right) 130 | assert str(expr) == "(1 + 2)" 131 | 132 | def test2(): 133 | left = add(V(1), add(V(1), C(1))) 134 | right = add(add(C(2), V(2)), add(V(3), V(2))) 135 | varmap, expr = unify(left, right) 136 | assert str(expr) == "((2 + 1) + ((2 + 1) + 1))" 137 | 138 | def test3(): 139 | left = add(V(1), V(1)) 140 | right = add(C(1), C(2)) 141 | try: 142 | unify(left, right) 143 | raise RuntimeError("expected fail") 144 | except ValueError: 145 | pass 146 | 147 | 148 | test1() 149 | test2() 150 | test3() 151 | -------------------------------------------------------------------------------- /replit.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: { 2 | deps = [ 3 | pkgs.cowsay 4 | ]; 5 | } -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /rust/almost_palindrome.rs: -------------------------------------------------------------------------------- 1 | // Whether the sequence from s[i] to s[j] inclusive is a palindrome 2 | fn is_palindrome(s: &[char]) -> bool { 3 | if s.len() < 2 { 4 | return true; 5 | } 6 | let last = s.len() - 1; 7 | if s[0] != s[last] { 8 | return false; 9 | } 10 | is_palindrome(&s[1..last]) 11 | } 12 | 13 | fn is_almost_palindrome(s: &[char]) -> bool { 14 | if s.len() < 3 { 15 | return true; 16 | } 17 | let last = s.len() - 1; 18 | if s[0] == s[last] { 19 | return is_almost_palindrome(&s[1..last]); 20 | } 21 | is_palindrome(&s[1..]) || is_palindrome(&s[..last]) 22 | } 23 | 24 | fn test_almost_palindrome(s: &str) { 25 | let chars: Vec = s.chars().collect(); 26 | println!("is_almost_palindrome(\"{}\") = {}", s, is_almost_palindrome(&chars)); 27 | } 28 | 29 | fn main() { 30 | test_almost_palindrome(""); 31 | test_almost_palindrome("x"); 32 | test_almost_palindrome("oo"); 33 | test_almost_palindrome("boof"); 34 | test_almost_palindrome("boofb"); 35 | test_almost_palindrome("bbboofbbb"); 36 | test_almost_palindrome("fboofb"); 37 | test_almost_palindrome("pogop"); 38 | test_almost_palindrome("pogrop"); 39 | test_almost_palindrome("pogrop"); 40 | test_almost_palindrome("pogroop"); 41 | test_almost_palindrome("pogrrop"); 42 | } 43 | -------------------------------------------------------------------------------- /rust/guessing_game/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /rust/guessing_game/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cfg-if" 7 | version = "1.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 10 | 11 | [[package]] 12 | name = "getrandom" 13 | version = "0.2.8" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 16 | dependencies = [ 17 | "cfg-if", 18 | "libc", 19 | "wasi", 20 | ] 21 | 22 | [[package]] 23 | name = "guessing_game" 24 | version = "0.1.0" 25 | dependencies = [ 26 | "rand", 27 | ] 28 | 29 | [[package]] 30 | name = "libc" 31 | version = "0.2.137" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 34 | 35 | [[package]] 36 | name = "ppv-lite86" 37 | version = "0.2.17" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 40 | 41 | [[package]] 42 | name = "rand" 43 | version = "0.8.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 46 | dependencies = [ 47 | "libc", 48 | "rand_chacha", 49 | "rand_core", 50 | ] 51 | 52 | [[package]] 53 | name = "rand_chacha" 54 | version = "0.3.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 57 | dependencies = [ 58 | "ppv-lite86", 59 | "rand_core", 60 | ] 61 | 62 | [[package]] 63 | name = "rand_core" 64 | version = "0.6.4" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 67 | dependencies = [ 68 | "getrandom", 69 | ] 70 | 71 | [[package]] 72 | name = "wasi" 73 | version = "0.11.0+wasi-snapshot-preview1" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 76 | -------------------------------------------------------------------------------- /rust/guessing_game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "guessing_game" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | -------------------------------------------------------------------------------- /rust/guessing_game/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | 3 | use rand::Rng; 4 | use std::cmp::Ordering; 5 | use std::io; 6 | 7 | fn main() { 8 | println!("Guess the number."); 9 | 10 | let secret_number = rand::thread_rng().gen_range(1..101); 11 | 12 | loop { 13 | println!("Please input your guess."); 14 | 15 | let mut guess = String::new(); 16 | 17 | io::stdin() 18 | .read_line(&mut guess) 19 | .expect("Failed to read line."); 20 | 21 | let guess: u32 = match guess.trim().parse() { 22 | Ok(num) => num, 23 | Err(_) => continue, 24 | }; 25 | 26 | println!("You guessed: {}", guess); 27 | 28 | match guess.cmp(&secret_number) { 29 | Ordering::Less => println!("Too small!"), 30 | Ordering::Greater => println!("Too big!"), 31 | Ordering::Equal => { 32 | println!("You win!"); 33 | return; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rust/hello.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("hello rust world 2"); 3 | } 4 | --------------------------------------------------------------------------------