1 # bend-ml-nat-lemmas: lemas provados de Nat e List que a Base do Bend não tem. 2 # 3 # import bend-ml-nat-lemmas@0.1.0/main.bend as NL 4 # NL.add_comm(a, b) NL.mul_assoc(a, b, c) NL.product_append(xs, ys) ... 5 # 6 # Cada `law` afirma um fato; o `def` logo abaixo é a prova, checada pelo kernel. 7 # Como ler uma prova: ela é uma função cujo TIPO é a afirmação. 8 # - `match` faz análise de casos (o número é 0, ou é 1 + p). 9 # - Uma chamada da própria função é a hipótese de indução ("já sei que vale 10 # para p, então provo para 1 + p"). 11 # - `%e : P` reescreve o objetivo usando a igualdade `e`. 12 # - `{==}` fecha quando os dois lados já calculam para a mesma coisa. 13 # Um parâmetro leva `+` quando a prova o usa mais de uma vez. 14 15 import Base 16 17 # Produto dos elementos de uma lista de Nat; a lista vazia vale 1. 18 # É o "número de elementos" de um tensor cuja shape é a lista. 19 def product(xs: List<&2, Nat>) -> Nat: 20 match xs: 21 case Nil{}: 22 1n 23 case Con{h, t}: 24 Nat.mul(h, product(t)) 25 26 27 law add_zero: 28 for a: Nat 29 {Nat.add(a, 0n) == a : Nat} 30 31 # a + 0 == a 32 # Caso 0: 0 + 0 calcula para 0. Passo: usa a hipótese para p. 33 def add_zero(a): 34 match a: 35 case 0n: 36 {==} 37 case 1n+p: 38 %add_zero(p) : {1n+Nat.add(p, 0n) == 1n+_ : Nat} 39 {==} 40 41 law add_succ: 42 for a: Nat 43 for -b: Nat 44 {1n+Nat.add(a, b) == Nat.add(a, 1n+b) : Nat} 45 46 # 1 + (a + b) == a + (1 + b) 47 def add_succ(a, b): 48 match a: 49 case 0n: 50 {==} 51 case 1n+p: 52 %add_succ(p, b) : {2n+Nat.add(p, b) == 1n+_ : Nat} 53 {==} 54 55 law add_comm: 56 for a: Nat 57 for +b: Nat 58 {Nat.add(a, b) == Nat.add(b, a) : Nat} 59 60 # a + b == b + a 61 def add_comm(a, b): 62 match a: 63 case 0n: 64 %add_zero(b) : {_ == Nat.add(b, 0n) : Nat} 65 {==} 66 case 1n+p: 67 %add_succ(b, p) : {1n+Nat.add(p, b) == _ : Nat} 68 %add_comm(p, b) : {1n+Nat.add(p, b) == 1n+_ : Nat} 69 {==} 70 71 law add_assoc: 72 for a: Nat 73 for -b: Nat 74 for -c: Nat 75 {Nat.add(a, Nat.add(b, c)) == Nat.add(Nat.add(a, b), c) : Nat} 76 77 # a + (b + c) == (a + b) + c 78 def add_assoc(a, b, c): 79 match a: 80 case 0n: 81 {==} 82 case 1n+p: 83 %add_assoc(p, b, c) : {1n+Nat.add(p, Nat.add(b, c)) == 1n+_ : Nat} 84 {==} 85 86 law mul_zero: 87 for a: Nat 88 {Nat.mul(a, 0n) == 0n : Nat} 89 90 # a * 0 == 0 91 def mul_zero(a): 92 match a: 93 case 0n: 94 {==} 95 case 1n+p: 96 mul_zero(p) 97 98 # x + (y + z) == y + (x + z): troca os termos do meio de uma soma tripla. 99 # (Lema auxiliar; não é uma lei publicada.) 100 def add_swap(x: Nat, +y: Nat, +z: Nat) -> {Nat.add(x, Nat.add(y, z)) == Nat.add(y, Nat.add(x, z)) : Nat}: 101 match x: 102 case 0n: 103 {==} 104 case 1n+q: 105 %add_succ(y, Nat.add(q, z)) : {1n+Nat.add(q, Nat.add(y, z)) == _ : Nat} 106 %add_swap(q, y, z) : {1n+Nat.add(q, Nat.add(y, z)) == 1n+_ : Nat} 107 {==} 108 109 law mul_succ: 110 for +a: Nat 111 for +b: Nat 112 {Nat.mul(a, 1n+b) == Nat.add(a, Nat.mul(a, b)) : Nat} 113 114 # a * (1 + b) == a + a * b 115 def mul_succ(a, b): 116 match a: 117 case 0n: 118 {==} 119 case 1n++p: 120 %Equal.sym(Nat, Nat.mul(p, 1n+b), Nat.add(p, Nat.mul(p, b)), mul_succ(p, b)) : {1n+Nat.add(b, _) == 1n+Nat.add(p, Nat.add(b, Nat.mul(p, b))) : Nat} 121 %add_swap(b, p, Nat.mul(p, b)) : {1n+Nat.add(b, Nat.add(p, Nat.mul(p, b))) == 1n+_ : Nat} 122 {==} 123 124 law mul_comm: 125 for +a: Nat 126 for +b: Nat 127 {Nat.mul(a, b) == Nat.mul(b, a) : Nat} 128 129 # a * b == b * a 130 def mul_comm(a, b): 131 match a: 132 case 0n: 133 %mul_zero(b) : {_ == Nat.mul(b, 0n) : Nat} 134 {==} 135 case 1n++p: 136 %Equal.sym(Nat, Nat.mul(b, 1n+p), Nat.add(b, Nat.mul(b, p)), mul_succ(b, p)) : {Nat.add(b, Nat.mul(p, b)) == _ : Nat} 137 %mul_comm(p, b) : {Nat.add(b, Nat.mul(p, b)) == Nat.add(b, _) : Nat} 138 {==} 139 140 law mul_dist: 141 for a: Nat 142 for -b: Nat 143 for +c: Nat 144 {Nat.add(Nat.mul(a, c), Nat.mul(b, c)) == Nat.mul(Nat.add(a, b), c) : Nat} 145 146 # a * c + b * c == (a + b) * c 147 def mul_dist(a, b, c): 148 match a: 149 case 0n: 150 {==} 151 case 1n+p: 152 %mul_dist(p, b, c) : {Nat.add(Nat.add(c, Nat.mul(p, c)), Nat.mul(b, c)) == Nat.add(c, _) : Nat} 153 %add_assoc(c, Nat.mul(p, c), Nat.mul(b, c)) : {_ == Nat.add(c, Nat.add(Nat.mul(p, c), Nat.mul(b, c))) : Nat} 154 {==} 155 156 law mul_assoc: 157 for +a: Nat 158 for +b: Nat 159 for +c: Nat 160 {Nat.mul(a, Nat.mul(b, c)) == Nat.mul(Nat.mul(a, b), c) : Nat} 161 162 # a * (b * c) == (a * b) * c 163 # Passo: (1+p)*(b*c) = b*c + p*(b*c). A hipótese diz p*(b*c) == (p*b)*c, e a 164 # distributividade diz b*c + (p*b)*c == (b + p*b)*c, que é o lado direito. 165 def mul_assoc(a, b, c): 166 match a: 167 case 0n: 168 {==} 169 case 1n++p: 170 %Equal.sym(Nat, Nat.mul(p, Nat.mul(b, c)), Nat.mul(Nat.mul(p, b), c), mul_assoc(p, b, c)) : {Nat.add(Nat.mul(b, c), _) == Nat.mul(Nat.add(b, Nat.mul(p, b)), c) : Nat} 171 mul_dist(b, Nat.mul(p, b), c) 172 173 law mul_one_l: 174 for +a: Nat 175 {Nat.mul(1n, a) == a : Nat} 176 177 # 1 * a == a 178 # 1 * a calcula para a + 0, e já provamos que a + 0 == a. 179 def mul_one_l(a): 180 add_zero(a) 181 182 law mul_one_r: 183 for +a: Nat 184 {Nat.mul(a, 1n) == a : Nat} 185 186 # a * 1 == a 187 def mul_one_r(a): 188 match a: 189 case 0n: 190 {==} 191 case 1n++p: 192 %mul_one_r(p) : {1n+Nat.mul(p, 1n) == 1n+_ : Nat} 193 {==} 194 195 law append_nil: 196 for -A: Data 197 for xs: List<&2, A> 198 {List.append(&2, A, xs, Nil{}) == xs : List<&2, A>} 199 200 # xs ++ [] == xs 201 def append_nil(A, xs): 202 match xs: 203 case Nil{}: 204 {==} 205 case Con{h, t}: 206 %append_nil(A, t) : {Con{h, List.append(&2, A, t, Nil{})} == Con{h, _} : List<&2, A>} 207 {==} 208 209 law append_assoc: 210 for -A: Data 211 for xs: List<&2, A> 212 for ys: List<&2, A> 213 for zs: List<&2, A> 214 {List.append(&2, A, List.append(&2, A, xs, ys), zs) == List.append(&2, A, xs, List.append(&2, A, ys, zs)) : List<&2, A>} 215 216 # (xs ++ ys) ++ zs == xs ++ (ys ++ zs) 217 def append_assoc(A, xs, ys, zs): 218 match xs: 219 case Nil{}: 220 {==} 221 case Con{h, t}: 222 %append_assoc(A, t, ys, zs) : {Con{h, List.append(&2, A, List.append(&2, A, t, ys), zs)} == Con{h, _} : List<&2, A>} 223 {==} 224 225 law length_append: 226 for -A: Data 227 for xs: List<&2, A> 228 for ys: List<&2, A> 229 {List.length(&2, A, List.append(&2, A, xs, ys)) == Nat.add(List.length(&2, A, xs), List.length(&2, A, ys)) : Nat} 230 231 # length (xs ++ ys) == length xs + length ys 232 def length_append(A, xs, ys): 233 match xs: 234 case Nil{}: 235 {==} 236 case Con{h, t}: 237 %length_append(A, t, ys) : {1n+List.length(&2, A, List.append(&2, A, t, ys)) == 1n+_ : Nat} 238 {==} 239 240 law product_append: 241 for +xs: List<&2, Nat> 242 for +ys: List<&2, Nat> 243 {product(List.append(&2, Nat, xs, ys)) == Nat.mul(product(xs), product(ys)) : Nat} 244 245 # product (xs ++ ys) == product xs * product ys 246 # Nil: product ys == 1 * product ys (pela lei mul_one_l, lida ao contrário). 247 # Con: product(h :: t ++ ys) = h * product(t ++ ys); a hipótese troca isso por 248 # h * (product t * product ys), e mul_assoc fecha. 249 def product_append(xs, ys): 250 match xs: 251 case Nil{}: 252 Equal.sym(Nat, Nat.mul(1n, product(ys)), product(ys), mul_one_l(product(ys))) 253 case Con{+h, +t}: 254 %Equal.sym(Nat, product(List.append(&2, Nat, t, ys)), Nat.mul(product(t), product(ys)), product_append(t, ys)) : {Nat.mul(h, _) == Nat.mul(Nat.mul(h, product(t)), product(ys)) : Nat} 255 mul_assoc(h, product(t), product(ys))