1 # bend-mathlib/algebra.bend: abstract associativity/commutativity theorems and their Nat/Bool/List instances. 2 import Base 3 import ./nat.bend as MNat 4 import ./bool.bend as MBool 5 import ./list.bend as MList 6 7 # Four-way reassociation from associativity alone. 8 law op_assoc4: 9 for ~A: Data 10 for ~op: A -> A -> A 11 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 12 for +a: A 13 for +b: A 14 for +c: A 15 for +d: A 16 {op(op(op(a, b), c), d) == op(a, op(b, op(c, d))) : A} 17 18 def op_assoc4(A, op, assoc, a, b, c, d): 19 %assoc(a, b, op(c, d)) : {op(op(op(a, b), c), d) == _ : A} 20 assoc(op(a, b), c, d) 21 22 # Left commutation from associativity and commutativity. 23 law op_left_comm: 24 for ~A: Data 25 for ~op: A -> A -> A 26 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 27 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 28 for +a: A 29 for +b: A 30 for +c: A 31 {op(a, op(b, c)) == op(b, op(a, c)) : A} 32 33 def op_left_comm(A, op, assoc, comm, a, b, c): 34 %assoc(a, b, c) : {_ == op(b, op(a, c)) : A} 35 %Equal.sym(A, op(a, b), op(b, a), comm(a, b)) : {op(_, c) == op(b, op(a, c)) : A} 36 assoc(b, a, c) 37 38 # Right commutation from associativity and commutativity. 39 law op_right_comm: 40 for ~A: Data 41 for ~op: A -> A -> A 42 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 43 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 44 for +a: A 45 for +b: A 46 for +c: A 47 {op(op(a, b), c) == op(op(a, c), b) : A} 48 49 def op_right_comm(A, op, assoc, comm, a, b, c): 50 %Equal.sym(A, op(op(a, b), c), op(a, op(b, c)), assoc(a, b, c)) : {_ == op(op(a, c), b) : A} 51 %Equal.sym(A, op(op(a, c), b), op(a, op(c, b)), assoc(a, c, b)) : {op(a, op(b, c)) == _ : A} 52 %Equal.sym(A, op(c, b), op(b, c), comm(c, b)) : {op(a, op(b, c)) == op(a, _) : A} 53 {==} 54 55 # Middle-four interchange from associativity and commutativity. 56 law op_four: 57 for ~A: Data 58 for ~op: A -> A -> A 59 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 60 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 61 for +a: A 62 for +b: A 63 for +c: A 64 for +d: A 65 {op(op(a, b), op(c, d)) == op(op(a, c), op(b, d)) : A} 66 67 def op_four(A, op, assoc, comm, a, b, c, d): 68 %Equal.sym(A, op(op(a, b), op(c, d)), op(a, op(b, op(c, d))), assoc(a, b, op(c, d))) : {_ == op(op(a, c), op(b, d)) : A} 69 %Equal.sym(A, op(op(a, c), op(b, d)), op(a, op(c, op(b, d))), assoc(a, c, op(b, d))) : {op(a, op(b, op(c, d))) == _ : A} 70 %op_left_comm(A, op, assoc, comm, b, c, d) : {op(a, op(b, op(c, d))) == op(a, _) : A} 71 {==} 72 73 # Three-way commutation from commutativity alone. 74 law op_comm3: 75 for ~A: Data 76 for ~op: A -> A -> A 77 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 78 for +a: A 79 for +b: A 80 for +c: A 81 {op(op(a, b), c) == op(c, op(b, a)) : A} 82 83 def op_comm3(A, op, comm, a, b, c): 84 %Equal.sym(A, op(op(a, b), c), op(c, op(a, b)), comm(op(a, b), c)) : {_ == op(c, op(b, a)) : A} 85 %Equal.sym(A, op(b, a), op(a, b), comm(b, a)) : {op(c, op(a, b)) == op(c, _) : A} 86 {==} 87 88 def internal_nat_add_assoc(x: Nat, y: Nat, z: Nat) -> {Nat.add(Nat.add(x, y), z) == Nat.add(x, Nat.add(y, z)) : Nat}: 89 MNat.add_assoc(x, y, z) 90 91 def internal_nat_add_comm(x: Nat, y: Nat) -> {Nat.add(x, y) == Nat.add(y, x) : Nat}: 92 MNat.add_comm(x, y) 93 94 def internal_nat_mul(x: Nat, y: Nat) -> Nat: 95 Nat.mul(x, y) 96 97 def internal_nat_mul_assoc(x: Nat, y: Nat, z: Nat) -> {Nat.mul(Nat.mul(x, y), z) == Nat.mul(x, Nat.mul(y, z)) : Nat}: 98 MNat.mul_assoc(x, y, z) 99 100 def internal_nat_mul_comm(x: Nat, y: Nat) -> {Nat.mul(x, y) == Nat.mul(y, x) : Nat}: 101 MNat.mul_comm(x, y) 102 103 def internal_bool_and_assoc(x: Bool, y: Bool, z: Bool) -> {Bool.and(Bool.and(x, y), z) == Bool.and(x, Bool.and(y, z)) : Bool}: 104 MBool.and_assoc(x, y, z) 105 106 def internal_bool_and_comm(x: Bool, y: Bool) -> {Bool.and(x, y) == Bool.and(y, x) : Bool}: 107 MBool.and_comm(x, y) 108 109 def internal_bool_or_assoc(x: Bool, y: Bool, z: Bool) -> {Bool.or(Bool.or(x, y), z) == Bool.or(x, Bool.or(y, z)) : Bool}: 110 MBool.or_assoc(x, y, z) 111 112 def internal_bool_or_comm(x: Bool, y: Bool) -> {Bool.or(x, y) == Bool.or(y, x) : Bool}: 113 MBool.or_comm(x, y) 114 115 def internal_list_nat_append_assoc(x: List<&2, Nat>, y: List<&2, Nat>, z: List<&2, Nat>) -> {List.append(&2, Nat, List.append(&2, Nat, x, y), z) == List.append(&2, Nat, x, List.append(&2, Nat, y, z)) : List<&2, Nat>}: 116 MList.append_assoc(&2, Nat, x, y, z) 117 118 # Nat addition is left-commutative (the same statement as MNat.add_left_comm). 119 law nat_add_left_comm: 120 for +a: Nat 121 for +b: Nat 122 for +c: Nat 123 {Nat.add(a, Nat.add(b, c)) == Nat.add(b, Nat.add(a, c)) : Nat} 124 125 def nat_add_left_comm(a, b, c): 126 op_left_comm(~Nat, ~Nat.add, ~internal_nat_add_assoc, ~internal_nat_add_comm, a, b, c) 127 128 # Nat addition is right-commutative (the same statement as MNat.add_right_comm). 129 law nat_add_right_comm: 130 for +a: Nat 131 for +b: Nat 132 for +c: Nat 133 {Nat.add(Nat.add(a, b), c) == Nat.add(Nat.add(a, c), b) : Nat} 134 135 def nat_add_right_comm(a, b, c): 136 op_right_comm(~Nat, ~Nat.add, ~internal_nat_add_assoc, ~internal_nat_add_comm, a, b, c) 137 138 # Nat addition's middle-four interchange (the same statement as MNat.add_add_add_comm). 139 law nat_add_four: 140 for +a: Nat 141 for +b: Nat 142 for +c: Nat 143 for +d: Nat 144 {Nat.add(Nat.add(a, b), Nat.add(c, d)) == Nat.add(Nat.add(a, c), Nat.add(b, d)) : Nat} 145 146 def nat_add_four(a, b, c, d): 147 op_four(~Nat, ~Nat.add, ~internal_nat_add_assoc, ~internal_nat_add_comm, a, b, c, d) 148 149 # Nat multiplication is left-commutative (the same statement as MNat.mul_left_comm). 150 law nat_mul_left_comm: 151 for +a: Nat 152 for +b: Nat 153 for +c: Nat 154 {Nat.mul(a, Nat.mul(b, c)) == Nat.mul(b, Nat.mul(a, c)) : Nat} 155 156 def nat_mul_left_comm(a, b, c): 157 op_left_comm(~Nat, ~internal_nat_mul, ~internal_nat_mul_assoc, ~internal_nat_mul_comm, a, b, c) 158 159 # Nat multiplication is right-commutative (the same statement as MNat.mul_right_comm). 160 law nat_mul_right_comm: 161 for +a: Nat 162 for +b: Nat 163 for +c: Nat 164 {Nat.mul(Nat.mul(a, b), c) == Nat.mul(Nat.mul(a, c), b) : Nat} 165 166 def nat_mul_right_comm(a, b, c): 167 op_right_comm(~Nat, ~internal_nat_mul, ~internal_nat_mul_assoc, ~internal_nat_mul_comm, a, b, c) 168 169 # Nat multiplication's middle-four interchange (the same statement as MNat.mul_mul_mul_comm). 170 law nat_mul_four: 171 for +a: Nat 172 for +b: Nat 173 for +c: Nat 174 for +d: Nat 175 {Nat.mul(Nat.mul(a, b), Nat.mul(c, d)) == Nat.mul(Nat.mul(a, c), Nat.mul(b, d)) : Nat} 176 177 def nat_mul_four(a, b, c, d): 178 op_four(~Nat, ~internal_nat_mul, ~internal_nat_mul_assoc, ~internal_nat_mul_comm, a, b, c, d) 179 180 # Bool and is left-commutative. 181 law bool_and_left_comm: 182 for +a: Bool 183 for +b: Bool 184 for +c: Bool 185 {Bool.and(a, Bool.and(b, c)) == Bool.and(b, Bool.and(a, c)) : Bool} 186 187 def bool_and_left_comm(a, b, c): 188 op_left_comm(~Bool, ~Bool.and, ~internal_bool_and_assoc, ~internal_bool_and_comm, a, b, c) 189 190 # Bool and is right-commutative. 191 law bool_and_right_comm: 192 for +a: Bool 193 for +b: Bool 194 for +c: Bool 195 {Bool.and(Bool.and(a, b), c) == Bool.and(Bool.and(a, c), b) : Bool} 196 197 def bool_and_right_comm(a, b, c): 198 op_right_comm(~Bool, ~Bool.and, ~internal_bool_and_assoc, ~internal_bool_and_comm, a, b, c) 199 200 # Bool or is left-commutative. 201 law bool_or_left_comm: 202 for +a: Bool 203 for +b: Bool 204 for +c: Bool 205 {Bool.or(a, Bool.or(b, c)) == Bool.or(b, Bool.or(a, c)) : Bool} 206 207 def bool_or_left_comm(a, b, c): 208 op_left_comm(~Bool, ~Bool.or, ~internal_bool_or_assoc, ~internal_bool_or_comm, a, b, c) 209 210 # Bool or is right-commutative. 211 law bool_or_right_comm: 212 for +a: Bool 213 for +b: Bool 214 for +c: Bool 215 {Bool.or(Bool.or(a, b), c) == Bool.or(Bool.or(a, c), b) : Bool} 216 217 def bool_or_right_comm(a, b, c): 218 op_right_comm(~Bool, ~Bool.or, ~internal_bool_or_assoc, ~internal_bool_or_comm, a, b, c) 219 220 # Four-way reassociation for List append over Nat. 221 law list_nat_append_assoc4: 222 for +a: List<&2, Nat> 223 for +b: List<&2, Nat> 224 for +c: List<&2, Nat> 225 for +d: List<&2, Nat> 226 {List.append(&2, Nat, List.append(&2, Nat, List.append(&2, Nat, a, b), c), d) == List.append(&2, Nat, a, List.append(&2, Nat, b, List.append(&2, Nat, c, d))) : List<&2, Nat>} 227 228 def list_nat_append_assoc4(a, b, c, d): 229 op_assoc4(~List<&2, Nat>, ~List.append(&2, Nat), ~internal_list_nat_append_assoc, a, b, c, d) 230 231 def internal_foldl_foldr(~B: Data, ~op: B -> B -> B, ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B}, ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B}, ~z: B, ~id: @x: B -> {op(z, x) == x : B}, +xs: List<&2, B>, +acc: B) -> {List.foldl(&2, B, B, op, xs, acc) == op(acc, List.foldr(&2, B, B, op, xs, z)) : B}: 232 match xs: 233 case Nil{}: 234 Equal.sym(B, op(acc, z), acc, Equal.trans(B, op(acc, z), op(z, acc), acc, comm(acc, z), id(acc))) 235 case +h <> +t: 236 %Equal.sym(B, List.foldl(&2, B, B, op, t, op(acc, h)), op(op(acc, h), List.foldr(&2, B, B, op, t, z)), internal_foldl_foldr(~B, ~op, ~assoc, ~comm, ~z, ~id, t, op(acc, h))) : {_ == op(acc, op(h, List.foldr(&2, B, B, op, t, z))) : B} 237 assoc(acc, h, List.foldr(&2, B, B, op, t, z)) 238 239 # Folding left equals folding right for an associative, commutative operation with a left identity (foldl_eq_foldr needs no identity). 240 law foldl_op_eq_foldr_op: 241 for ~B: Data 242 for ~op: B -> B -> B 243 for ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B} 244 for ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B} 245 for ~z: B 246 for ~id: @x: B -> {op(z, x) == x : B} 247 for +xs: List<&2, B> 248 {List.foldl(&2, B, B, op, xs, z) == List.foldr(&2, B, B, op, xs, z) : B} 249 250 def foldl_op_eq_foldr_op(B, op, assoc, comm, z, id, xs): 251 %Equal.sym(B, List.foldl(&2, B, B, op, xs, z), op(z, List.foldr(&2, B, B, op, xs, z)), internal_foldl_foldr(~B, ~op, ~assoc, ~comm, ~z, ~id, xs, z)) : {_ == List.foldr(&2, B, B, op, xs, z) : B} 252 id(List.foldr(&2, B, B, op, xs, z)) 253 254 def internal_foldl_assoc(~B: Data, ~op: B -> B -> B, ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B}, +xs: List<&2, B>, +a: B, +b: B) -> {List.foldl(&2, B, B, op, xs, op(a, b)) == op(a, List.foldl(&2, B, B, op, xs, b)) : B}: 255 match xs: 256 case Nil{}: 257 {==} 258 case +h <> +t: 259 %Equal.sym(B, op(op(a, b), h), op(a, op(b, h)), assoc(a, b, h)) : {List.foldl(&2, B, B, op, t, _) == op(a, List.foldl(&2, B, B, op, t, op(b, h))) : B} 260 internal_foldl_assoc(~B, ~op, ~assoc, t, a, op(b, h)) 261 262 def internal_foldl_eq_foldr(~B: Data, ~op: B -> B -> B, ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B}, ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B}, +z: B, +xs: List<&2, B>) -> {List.foldl(&2, B, B, op, xs, z) == List.foldr(&2, B, B, op, xs, z) : B}: 263 match xs: 264 case Nil{}: 265 {==} 266 case +h <> +t: 267 %comm(h, z) : {List.foldl(&2, B, B, op, t, _) == op(h, List.foldr(&2, B, B, op, t, z)) : B} 268 %Equal.sym(B, List.foldl(&2, B, B, op, t, op(h, z)), op(h, List.foldl(&2, B, B, op, t, z)), internal_foldl_assoc(~B, ~op, ~assoc, t, h, z)) : {_ == op(h, List.foldr(&2, B, B, op, t, z)) : B} 269 %internal_foldl_eq_foldr(~B, ~op, ~assoc, ~comm, z, t) : {op(h, List.foldl(&2, B, B, op, t, z)) == op(h, _) : B} 270 {==} 271 272 # Folding left equals folding right for an associative, commutative operation (Mathlib's List.foldl_eq_foldr). 273 law foldl_eq_foldr: 274 for ~B: Data 275 for ~op: B -> B -> B 276 for ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B} 277 for ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B} 278 for +z: B 279 for +xs: List<&2, B> 280 {List.foldl(&2, B, B, op, xs, z) == List.foldr(&2, B, B, op, xs, z) : B} 281 282 def foldl_eq_foldr(B, op, assoc, comm, z, xs): 283 internal_foldl_eq_foldr(~B, ~op, ~assoc, ~comm, z, xs) 284 285 # --- generated: _sym twins (tools/mathlib/twins.ts), do not edit --- 286 287 # Four-way reassociation from associativity alone, reversed to rewrite toward the simple side. 288 law op_assoc4_sym: 289 for ~A: Data 290 for ~op: A -> A -> A 291 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 292 for +a: A 293 for +b: A 294 for +c: A 295 for +d: A 296 {op(a, op(b, op(c, d))) == op(op(op(a, b), c), d) : A} 297 298 def op_assoc4_sym(A, op, assoc, a, b, c, d): 299 Equal.sym(A, op(op(op(a, b), c), d), op(a, op(b, op(c, d))), op_assoc4(~A, ~op, ~assoc, a, b, c, d)) 300 301 # Left commutation from associativity and commutativity, reversed to rewrite toward the simple side. 302 law op_left_comm_sym: 303 for ~A: Data 304 for ~op: A -> A -> A 305 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 306 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 307 for +a: A 308 for +b: A 309 for +c: A 310 {op(b, op(a, c)) == op(a, op(b, c)) : A} 311 312 def op_left_comm_sym(A, op, assoc, comm, a, b, c): 313 Equal.sym(A, op(a, op(b, c)), op(b, op(a, c)), op_left_comm(~A, ~op, ~assoc, ~comm, a, b, c)) 314 315 # Right commutation from associativity and commutativity, reversed to rewrite toward the simple side. 316 law op_right_comm_sym: 317 for ~A: Data 318 for ~op: A -> A -> A 319 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 320 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 321 for +a: A 322 for +b: A 323 for +c: A 324 {op(op(a, c), b) == op(op(a, b), c) : A} 325 326 def op_right_comm_sym(A, op, assoc, comm, a, b, c): 327 Equal.sym(A, op(op(a, b), c), op(op(a, c), b), op_right_comm(~A, ~op, ~assoc, ~comm, a, b, c)) 328 329 # Middle-four interchange from associativity and commutativity, reversed to rewrite toward the simple side. 330 law op_four_sym: 331 for ~A: Data 332 for ~op: A -> A -> A 333 for ~assoc: @x: A -> @y: A -> @z: A -> {op(op(x, y), z) == op(x, op(y, z)) : A} 334 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 335 for +a: A 336 for +b: A 337 for +c: A 338 for +d: A 339 {op(op(a, c), op(b, d)) == op(op(a, b), op(c, d)) : A} 340 341 def op_four_sym(A, op, assoc, comm, a, b, c, d): 342 Equal.sym(A, op(op(a, b), op(c, d)), op(op(a, c), op(b, d)), op_four(~A, ~op, ~assoc, ~comm, a, b, c, d)) 343 344 # Three-way commutation from commutativity alone, reversed to rewrite toward the simple side. 345 law op_comm3_sym: 346 for ~A: Data 347 for ~op: A -> A -> A 348 for ~comm: @x: A -> @y: A -> {op(x, y) == op(y, x) : A} 349 for +a: A 350 for +b: A 351 for +c: A 352 {op(c, op(b, a)) == op(op(a, b), c) : A} 353 354 def op_comm3_sym(A, op, comm, a, b, c): 355 Equal.sym(A, op(op(a, b), c), op(c, op(b, a)), op_comm3(~A, ~op, ~comm, a, b, c)) 356 357 # Nat addition is left-commutative (the same statement as MNat.add_left_comm), reversed to rewrite toward the simple side. 358 law nat_add_left_comm_sym: 359 for +a: Nat 360 for +b: Nat 361 for +c: Nat 362 {Nat.add(b, Nat.add(a, c)) == Nat.add(a, Nat.add(b, c)) : Nat} 363 364 def nat_add_left_comm_sym(a, b, c): 365 Equal.sym(Nat, Nat.add(a, Nat.add(b, c)), Nat.add(b, Nat.add(a, c)), nat_add_left_comm(a, b, c)) 366 367 # Nat addition is right-commutative (the same statement as MNat.add_right_comm), reversed to rewrite toward the simple side. 368 law nat_add_right_comm_sym: 369 for +a: Nat 370 for +b: Nat 371 for +c: Nat 372 {Nat.add(Nat.add(a, c), b) == Nat.add(Nat.add(a, b), c) : Nat} 373 374 def nat_add_right_comm_sym(a, b, c): 375 Equal.sym(Nat, Nat.add(Nat.add(a, b), c), Nat.add(Nat.add(a, c), b), nat_add_right_comm(a, b, c)) 376 377 # Nat addition's middle-four interchange (the same statement as MNat.add_add_add_comm), reversed to rewrite toward the simple side. 378 law nat_add_four_sym: 379 for +a: Nat 380 for +b: Nat 381 for +c: Nat 382 for +d: Nat 383 {Nat.add(Nat.add(a, c), Nat.add(b, d)) == Nat.add(Nat.add(a, b), Nat.add(c, d)) : Nat} 384 385 def nat_add_four_sym(a, b, c, d): 386 Equal.sym(Nat, Nat.add(Nat.add(a, b), Nat.add(c, d)), Nat.add(Nat.add(a, c), Nat.add(b, d)), nat_add_four(a, b, c, d)) 387 388 # Nat multiplication is left-commutative (the same statement as MNat.mul_left_comm), reversed to rewrite toward the simple side. 389 law nat_mul_left_comm_sym: 390 for +a: Nat 391 for +b: Nat 392 for +c: Nat 393 {Nat.mul(b, Nat.mul(a, c)) == Nat.mul(a, Nat.mul(b, c)) : Nat} 394 395 def nat_mul_left_comm_sym(a, b, c): 396 Equal.sym(Nat, Nat.mul(a, Nat.mul(b, c)), Nat.mul(b, Nat.mul(a, c)), nat_mul_left_comm(a, b, c)) 397 398 # Nat multiplication is right-commutative (the same statement as MNat.mul_right_comm), reversed to rewrite toward the simple side. 399 law nat_mul_right_comm_sym: 400 for +a: Nat 401 for +b: Nat 402 for +c: Nat 403 {Nat.mul(Nat.mul(a, c), b) == Nat.mul(Nat.mul(a, b), c) : Nat} 404 405 def nat_mul_right_comm_sym(a, b, c): 406 Equal.sym(Nat, Nat.mul(Nat.mul(a, b), c), Nat.mul(Nat.mul(a, c), b), nat_mul_right_comm(a, b, c)) 407 408 # Nat multiplication's middle-four interchange (the same statement as MNat.mul_mul_mul_comm), reversed to rewrite toward the simple side. 409 law nat_mul_four_sym: 410 for +a: Nat 411 for +b: Nat 412 for +c: Nat 413 for +d: Nat 414 {Nat.mul(Nat.mul(a, c), Nat.mul(b, d)) == Nat.mul(Nat.mul(a, b), Nat.mul(c, d)) : Nat} 415 416 def nat_mul_four_sym(a, b, c, d): 417 Equal.sym(Nat, Nat.mul(Nat.mul(a, b), Nat.mul(c, d)), Nat.mul(Nat.mul(a, c), Nat.mul(b, d)), nat_mul_four(a, b, c, d)) 418 419 # Bool and is left-commutative, reversed to rewrite toward the simple side. 420 law bool_and_left_comm_sym: 421 for +a: Bool 422 for +b: Bool 423 for +c: Bool 424 {Bool.and(b, Bool.and(a, c)) == Bool.and(a, Bool.and(b, c)) : Bool} 425 426 def bool_and_left_comm_sym(a, b, c): 427 Equal.sym(Bool, Bool.and(a, Bool.and(b, c)), Bool.and(b, Bool.and(a, c)), bool_and_left_comm(a, b, c)) 428 429 # Bool and is right-commutative, reversed to rewrite toward the simple side. 430 law bool_and_right_comm_sym: 431 for +a: Bool 432 for +b: Bool 433 for +c: Bool 434 {Bool.and(Bool.and(a, c), b) == Bool.and(Bool.and(a, b), c) : Bool} 435 436 def bool_and_right_comm_sym(a, b, c): 437 Equal.sym(Bool, Bool.and(Bool.and(a, b), c), Bool.and(Bool.and(a, c), b), bool_and_right_comm(a, b, c)) 438 439 # Bool or is left-commutative, reversed to rewrite toward the simple side. 440 law bool_or_left_comm_sym: 441 for +a: Bool 442 for +b: Bool 443 for +c: Bool 444 {Bool.or(b, Bool.or(a, c)) == Bool.or(a, Bool.or(b, c)) : Bool} 445 446 def bool_or_left_comm_sym(a, b, c): 447 Equal.sym(Bool, Bool.or(a, Bool.or(b, c)), Bool.or(b, Bool.or(a, c)), bool_or_left_comm(a, b, c)) 448 449 # Bool or is right-commutative, reversed to rewrite toward the simple side. 450 law bool_or_right_comm_sym: 451 for +a: Bool 452 for +b: Bool 453 for +c: Bool 454 {Bool.or(Bool.or(a, c), b) == Bool.or(Bool.or(a, b), c) : Bool} 455 456 def bool_or_right_comm_sym(a, b, c): 457 Equal.sym(Bool, Bool.or(Bool.or(a, b), c), Bool.or(Bool.or(a, c), b), bool_or_right_comm(a, b, c)) 458 459 # Four-way reassociation for List append over Nat, reversed to rewrite toward the simple side. 460 law list_nat_append_assoc4_sym: 461 for +a: List<&2, Nat> 462 for +b: List<&2, Nat> 463 for +c: List<&2, Nat> 464 for +d: List<&2, Nat> 465 {List.append(&2, Nat, a, List.append(&2, Nat, b, List.append(&2, Nat, c, d))) == List.append(&2, Nat, List.append(&2, Nat, List.append(&2, Nat, a, b), c), d) : List<&2, Nat>} 466 467 def list_nat_append_assoc4_sym(a, b, c, d): 468 Equal.sym(List<&2, Nat>, List.append(&2, Nat, List.append(&2, Nat, List.append(&2, Nat, a, b), c), d), List.append(&2, Nat, a, List.append(&2, Nat, b, List.append(&2, Nat, c, d))), list_nat_append_assoc4(a, b, c, d)) 469 470 # Folding left equals folding right for an associative, commutative operation with a left identity (foldl_eq_foldr needs no identity), reversed to rewrite toward the simple side. 471 law foldl_op_eq_foldr_op_sym: 472 for ~B: Data 473 for ~op: B -> B -> B 474 for ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B} 475 for ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B} 476 for ~z: B 477 for ~id: @x: B -> {op(z, x) == x : B} 478 for +xs: List<&2, B> 479 {List.foldr(&2, B, B, op, xs, z) == List.foldl(&2, B, B, op, xs, z) : B} 480 481 def foldl_op_eq_foldr_op_sym(B, op, assoc, comm, z, id, xs): 482 Equal.sym(B, List.foldl(&2, B, B, op, xs, z), List.foldr(&2, B, B, op, xs, z), foldl_op_eq_foldr_op(~B, ~op, ~assoc, ~comm, ~z, ~id, xs)) 483 484 # Folding left equals folding right for an associative, commutative operation (Mathlib's List.foldl_eq_foldr), reversed to rewrite toward the simple side. 485 law foldl_eq_foldr_sym: 486 for ~B: Data 487 for ~op: B -> B -> B 488 for ~assoc: @x: B -> @y: B -> @z: B -> {op(op(x, y), z) == op(x, op(y, z)) : B} 489 for ~comm: @x: B -> @y: B -> {op(x, y) == op(y, x) : B} 490 for +z: B 491 for +xs: List<&2, B> 492 {List.foldr(&2, B, B, op, xs, z) == List.foldl(&2, B, B, op, xs, z) : B} 493 494 def foldl_eq_foldr_sym(B, op, assoc, comm, z, xs): 495 Equal.sym(B, List.foldl(&2, B, B, op, xs, z), List.foldr(&2, B, B, op, xs, z), foldl_eq_foldr(~B, ~op, ~assoc, ~comm, z, xs))